#include "broadcast.h"
#include <Wt/WApplication.h>
#include <Wt/WAny.h>
#include <Wt/Json/Array.h>
#include <Wt/Json/Serializer.h>
#include <Wt/Json/Parser.h>
#include <Wt/Json/Object.h>
#include <Wt/Auth/HashFunction.h>
#include <Wt/WLocalDateTime.h>
#include <Wt/Utils.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>
#include <curl/curl.h>
#include <boost/algorithm/string.hpp>
#include <Magick++.h>

Broadcast::Broadcast(Wt::WServer *server):
        wtServer_(server) {
    downloadCountries();
    thread_ = std::thread(std::bind(&Broadcast::run, this));
    lastTimeoutCheck_ = Wt::WDateTime::currentDateTime();
    checkAndLogStart();
}

void Broadcast::checkAndLogStart() {
    const std::string logFilePath = "/opt/ypchat/logs/starts.log";
    std::ifstream infile(logFilePath);
    if (!infile.good()) {
        std::ofstream outfile(logFilePath);
        outfile.close();
    }
    infile.close();
    Wt::WDateTime now = Wt::WDateTime::currentDateTime();
    std::string timestamp = now.toString("yyyy-MM-dd HH:mm:ss").toUTF8();
    std::ofstream outfile(logFilePath, std::ios_base::app);
    if (outfile.is_open()) {
        outfile << timestamp << std::endl;
        outfile.close();
    } else {
        std::cerr << "Fehler beim Öffnen der Datei: " << logFilePath << std::endl;
    }
}

Broadcast::~Broadcast() {
    stop_ = true;
    thread_.join();
}

void Broadcast::connect(Client *client, const std::function<void ()> &fct) {
    connections_.push_back(std::make_unique<Connection>(Wt::WApplication::instance()->sessionId(), client, fct));
    auto userlistBroadcast = createUserList();
    auto newUserBroadcast = Wt::Json::Object{
        {"type", "newuser"},
        {"data", client->json()},
    };
    for (auto &connection: connections_) {
        connection->addBroadcast(userlistBroadcast);
        connection->addBroadcast(newUserBroadcast);
    }
    logClientLogin(client->json());
}

void Broadcast::logClientLogin(const Wt::Json::Object &clientJson) {
    const std::string logFilePath = "../logs/logins.log";
    Wt::Json::Array logArray;
    std::ifstream infile(logFilePath);
    if (infile.is_open()) {
        std::stringstream buffer;
        buffer << infile.rdbuf();
        std::string fileContent = buffer.str();
        infile.close();
        if (!fileContent.empty()) {
            Wt::Json::parse(fileContent, logArray);
        }
    }

    // Zeitstempel hinzufügen
    Wt::Json::Object logEntry = clientJson;
    Wt::WDateTime now = Wt::WDateTime::currentDateTime();
    std::string timestamp = now.toString("yyyy-MM-dd HH:mm:ss").toUTF8();
    logEntry["timestamp"] = Wt::Json::Value(timestamp);

    logArray.push_back(logEntry);
    std::ofstream outfile(logFilePath);
    if (outfile.is_open()) {
        outfile << Wt::Json::serialize(logArray) << std::endl;
        outfile.close();
    } else {
        std::cerr << "Fehler beim Öffnen der Datei: " << logFilePath << std::endl;
    }
}

void Broadcast::disconnect(Client *client) {
    std::unique_lock<std::mutex> lock(mutex_);
    for (unsigned int i = 0; i < connections_.size(); ++ i) {
        if (connections_[i]->client() == client) {
            connections_.erase(connections_.begin() + i);
            break;
        }
    }
    Wt::Json::Object leftBroadcast{
        {"type", "userleft"},
        {"data", Wt::Json::Value(client->userName)}
    };
    auto userlistBroadcast = createUserList();
    for (auto &connection: connections_) {
        connection->addBroadcast(userlistBroadcast);
        connection->addBroadcast(leftBroadcast);
    }
    auto sessionId = sessionIdForUserName(client->userName);
    for (auto it = conversations_.begin(); it != conversations_.end();) {
        if (it->first.find(sessionId + "/") == 0 || it->first.rfind("/" + sessionId) == it->first.size() - sessionId.length() - 1) {
            it = conversations_.erase(it);
        } else {
            ++it;
        }
    }
}

void Broadcast::addToDisconnectList(Client *client) {
    toDisconnect_.push_back(client);
}

Wt::Json::Object Broadcast::reSetUser(std::string oldSessionId, std::string newSessionId) {
    for (auto& connection : connections_) {
        if (connection->setSessionId(oldSessionId, newSessionId)) {
            Wt::Json::Object userData {
                {"gender", Wt::Json::Value(connection->gender())},
                {"country", Wt::Json::Value(connection->country())},
                {"iso-country-code", Wt::Json::Value(getCountryIsoCodeByCountry(connection->country()))},
                {"username", Wt::Json::Value(connection->userName())},
                {"age", Wt::Json::Value(connection->age())},
            };
            reSetSessionIdInMessages(oldSessionId, newSessionId);
            return userData;
        }
    }
    return {};
}

int Broadcast::count() const {
    std::unique_lock<std::mutex> lock(mutex_);
    return connections_.size();
}

std::string Broadcast::userNameForSessionId(std::string sessionId) {
    for (const auto &connection: connections_) {
        if (sessionId == connection->sessionId()) {
            return connection->userName();
        }
    }
    return "";
}

std::string Broadcast::sessionIdForUserName(std::string userName) {
    for (const auto &connection: connections_) {
        if (userName == connection->userName()) {
            return connection->sessionId();
        }
    }
    return "";
}

Wt::Json::Object Broadcast::userForSessionId(std::string sessionId) {
    for (const auto &connection: connections_) {
        if (sessionId == connection->sessionId()) {
            return connection->client()->json();
        }
    }
    return Wt::Json::Object{};
}

Wt::Json::Object Broadcast::userForUserName(std::string userName) {
    for (const auto &connection: connections_) {
        if (userName == connection->userName()) {
            return connection->client()->json();
        }
    }
    return Wt::Json::Object{};
}

void Broadcast::changeClientForSessionId(std::string sessionId, Client *client) {
    std::unique_lock<std::mutex> lock(mutex_);
    for (auto &connection: connections_) {
        if (sessionId == connection->sessionId()) {
            connection->setClient(client);
        }
    }
}

bool Broadcast::nameIsFree(std::string userNameToCheck) {
    std::unique_lock<std::mutex> lock(mutex_);
    for (auto &connection: connections_) {
        if (connection->userName() == userNameToCheck) {
            return false;
        }
    }
    return true;
}

void Broadcast::run() {
    Wt::Json::Object timedoutBroadcast {
        {"type", "timedout"},
        {"data", "dummy"}
    };
    for (;;) {
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
        for (auto clientToDisconnect: toDisconnect_) {
            disconnect(clientToDisconnect);
        }
        toDisconnect_.clear();
        if (stop_) {
            return;
        }
        std::unique_lock<std::mutex> lock(mutex_);
        for (auto &connection: connections_) {
            if (connection->client()->activitiesTimedOut()) {
                auto sessionId = connection->sessionId();
                toDisconnect_.push_back(connection->client());
                addMessageToSessionBroadcast(sessionId, timedoutBroadcast);
            }
            if (connection->getBroadcasts().size() == 0) {
                continue;
            }
            Wt::WServer::instance()->post(connection->sessionId(), connection->fct());
        }
    }
}

Wt::Json::Object Broadcast::logoutBroadcast() {
    return Wt::Json::Object{
        {"type", "logout"}
    };
}

void Broadcast::downloadCountries() {
    CURL* curl = curl_easy_init();
    if (!curl) {
        std::cerr << "Failed to initialize CURL." << std::endl;
    }
    curl_easy_setopt(curl, CURLOPT_URL, countriesDownloadUrl_.toUTF8().c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseData_);
    CURLcode res = curl_easy_perform(curl);
    curl_easy_cleanup(curl);
    if (res != CURLE_OK) {
        std::cerr << "Failed to download data: " << curl_easy_strerror(res) << std::endl;
    }
    parseCountriesData();
}

std::map<Wt::WString, Wt::WString> Broadcast::countries() {
    return countriesMap_;
}

Wt::WString Broadcast::getCountryIsoCodeByCountry(Wt::WString country) {
    for (const auto &countryItem: countriesMap_) {
        if (countryItem.first == country) {
            return countryItem.second;
        }
    }
    return "";
}

std::list<Wt::Json::Object> Broadcast::getBroadcastsForSession(std::string sessionId) {
    std::unique_lock<std::mutex> lock(mutex_);
    for (auto &connection: connections_) {
        if (connection->sessionId() == sessionId) {
            return connection->getBroadcasts(true);
        }
    }
    return std::list<Wt::Json::Object>();
}

Wt::Json::Object Broadcast::addMessage(std::string fromSessionId, std::string toUserName, Message message) {
    auto toSessionId = sessionIdForUserName(toUserName);
    if (toSessionId == "") {
        return Wt::Json::Object{
            {"type", "system"},
            {"data", "User left the chat."}
        };
    }
    auto fromUserName = userNameForSessionId(fromSessionId);
    if (blockings_[toUserName].find(fromUserName) != blockings_[toUserName].end()) {
        sendBlockedMessage(fromSessionId, toUserName);
    }
    auto id1 = fromSessionId + "/" + toSessionId;
    auto id2 = toSessionId + "/" + fromSessionId;
    auto it = conversations_.find(id1);
    if (it == conversations_.end()) {
        it = conversations_.find(id2);
    }
    if (it == conversations_.end()) {
        MessageQueue messageQueue;
        messageQueue.messages.push_back(message);
        auto [newIt, success] = conversations_.emplace(id1, MessageQueue{});
        it = newIt;
    }
    it->second.messages.push_back(message);
    it->second.user1Read = false;
    it->second.user2Read = false;
    sendMessageQueueToSession(fromSessionId, userNameForSessionId(fromSessionId), toUserName, it->second.messages);
    sendMessageQueueToSession(toSessionId, userNameForSessionId(fromSessionId), toUserName, it->second.messages);
    sendMessageCount(toSessionId);
    return Wt::Json::Object{
        {"type", "dummy"},
        {"data", "Message added to users queues."}
    };
}

Wt::Json::Object Broadcast::addImage(std::string fromSessionId, std::string toUserName, std::shared_ptr<Magick::Blob> image) {
    auto message = Message(fromSessionId, image);
    return addMessage(fromSessionId, toUserName, message);
}

void Broadcast::setConversationRead(std::string readingUserSessionId, std::string secondUserName) {
    auto toSessionId = sessionIdForUserName(secondUserName);
    std::unique_lock<std::mutex> lock(mutex_);
    if (toSessionId == "") {
        return;
    }
    auto id1 = readingUserSessionId + "/" + toSessionId;
    auto id2 = toSessionId + "/" + readingUserSessionId;
    auto it = conversations_.find(id1);
    if (it != conversations_.end()) {
        it->second.user1Read = true;
    } else {
        it = conversations_.find(id2);
        if (it != conversations_.end()) {
            it->second.user2Read = true;
        }
    }
    sendMessageCount(readingUserSessionId);
}

void Broadcast::sendConversation(std::string sessionId1, std::string userName2) {
    auto sessionId2 = sessionIdForUserName(userName2);
    auto userName1 = userNameForSessionId(sessionId1);
    std::unique_lock<std::mutex> lock(mutex_);
    auto id1 = sessionId1 + "/" + sessionId2;
    auto id2 = sessionId2 + "/" + sessionId1;
    auto it = conversations_.find(id1);
    if (it == conversations_.end()) {
        it = conversations_.find(id2);
        if (it == conversations_.end()) {
            return;
        }
    }
    sendMessageQueueToSession(sessionId1, userName1, userName2, it->second.messages);
}

void Broadcast::sendOpenConversations(std::string forSessionId) {
    std::unique_lock<std::mutex> lock(mutex_);
    Wt::Json::Array conversationsWithList;
    for (const auto &conversation: conversations_) {
        const auto key = conversation.first;
        const auto data = conversation.second;
        if ((key.compare(0, forSessionId.length() + 1, forSessionId + "/") == 0
                && !data.user1Read)
            || (key.compare(key.length() - forSessionId.length() - 1, forSessionId.length() + 1, "/" + forSessionId) == 0
                && !data.user2Read)) {
            std::size_t found = key.find("/");
            if (found != std::string::npos) {
                std::string firstSessionId = key.substr(0, found);
                std::string secondSessionId = key.substr(found + 1);
                auto userData = forSessionId == firstSessionId ? userForSessionId(secondSessionId) : userForSessionId(firstSessionId);
                conversationsWithList.push_back(userData);
            }
        }
    }
    Wt::Json::Object data{
        {"type", "openconversations"},
        {"data", conversationsWithList}
    };
    addMessageToSessionBroadcast(forSessionId, data);
}

void Broadcast::sendUserInformation(std::string sendToSessionId, std::string userName, std::string requestingUserName) {
    std::unique_lock<std::mutex> lock(mutex_);
    auto userData = userForUserName(userName);
    if (blockings_.find(userName) == blockings_.end()) {
        blockings_[userName] = std::set<std::string>();
    }
    userData["blocked"] = (blockings_.find(userName) != blockings_.end() && blockings_[userName].find(requestingUserName) != blockings_[userName].end());
    Wt::Json::Object broadcast{
        {"type", "userinfo"},
        {"data", userData}
    };
    addMessageToSessionBroadcast(sendToSessionId, broadcast);
}

void Broadcast::toggleBlockUser(std::string blockingUserName, std::string blockedUser, std::string blockingUserSessionId) {
    std::unique_lock<std::mutex> lock(mutex_);
    if (blockings_.find(blockedUser) == blockings_.end()) {
        blockings_[blockedUser] = std::set<std::string>();
    }
    if (blockings_[blockedUser].find(blockingUserName) != blockings_[blockedUser].end()) {
        blockings_[blockedUser].erase(blockingUserName);
        sendUnblockDone(blockingUserSessionId, blockedUser);
    } else {
        blockings_[blockedUser].insert(blockingUserName);
        sendBlockDone(blockingUserSessionId, blockedUser);
    }
}

void Broadcast::requestConversation(std::string sendToSessionId, std::string withUserName, std::string requestingUserName) {
    auto userData = userForUserName(withUserName);
    std::unique_lock<std::mutex> lock(mutex_);
    if (blockings_.find(withUserName) == blockings_.end()) {
        blockings_[withUserName] = std::set<std::string>();
    }
    userData["blocked"] = !(blockings_.find(withUserName) == blockings_.end() || blockings_[withUserName].find(requestingUserName) == blockings_[withUserName].end());
    auto sessionId2 = sessionIdForUserName(withUserName);
    auto id1 = sendToSessionId + "/" + sessionId2;
    auto id2 = sessionId2 + "/" + sendToSessionId;
    auto it = conversations_.find(id1);
    if (it == conversations_.end()) {
        it = conversations_.find(id2);
        if (it == conversations_.end()) {
            MessageQueue messageQueue;
            auto [newIt, success] = conversations_.emplace(id1, MessageQueue{});
            it = newIt;
        }
    }
    Wt::Json::Object broadcast{
        {"type", "conversation-start"},
        {"data", userData},
    };
    addMessageToSessionBroadcast(sendToSessionId, broadcast);
    Wt::Json::Array messagesJson;
    for (Broadcast::Message &message: it->second.messages) {
        messagesJson.push_back(message.json());
    }
    broadcast = {
        {"type", "messagequeue"},
        {"user1", Wt::Json::Value(requestingUserName)},
        {"user2", Wt::Json::Value(withUserName)},
        {"sessionid1", Wt::Json::Value(sendToSessionId)},
        {"sessionid2", Wt::Json::Value(sessionId2)},
        {"data", messagesJson}
    };
    addMessageToSessionBroadcast(sendToSessionId, broadcast);
}

void Broadcast::userSearch(std::string toSession, std::string nameIncludes, int minAge, int maxAge, std::unordered_set<std::string> countries, std::unordered_set<std::string> genders, std::string excludeName) {
    Wt::Json::Array searchResult;
    for (const auto &user: connections_) {
        if (
                (nameIncludes == "" || user->userName().find(nameIncludes) != std::string::npos)
                && (minAge <= user->age())
                && (maxAge >= user->age())
                && (countries.contains("All") || countries.contains(user->country()) || countries.size() == 0)
                && (genders.contains("All") || genders.contains(user->gender()) || genders.size() == 0)
                && (user->userName() != excludeName)
        ) {
            searchResult.push_back(user->client()->json());
        }
    }
    Wt::Json::Object broadcast{
        {"type", "search-result"},
        {"data", searchResult}
    };
    addMessageToSessionBroadcast(toSession, broadcast);
}

void Broadcast::sendHistory(std::string toSession) {
    std::unique_lock<std::mutex> lock(mutex_);
    Wt::Json::Array filteredConversationsPartners;
    for (const auto& pair : conversations_) {
        const std::string& key = pair.first;
        if (key.rfind(toSession + "/", 0) == 0 || key.find("/" + toSession) == key.size() - toSession.length() - 1) {
            std::string partnerId;
            if (key.rfind(toSession + "/", 0) == 0) {
                partnerId = key.substr(toSession.size() + 1);
            } else {
                partnerId = key.substr(0, key.size() - toSession.size() - 1);
            }
            auto partner = userForSessionId(partnerId);
            filteredConversationsPartners.push_back(Wt::Json::Value(partner));
        }
    }
    Wt::Json::Object broadcast{
        {"type", "history"},
        {"data", filteredConversationsPartners}
    };
    addMessageToSessionBroadcast(toSession, broadcast);
}

bool Broadcast::parseCountriesData() {
    std::istringstream iss(responseData_);
    std::string line;
    while (std::getline(iss, line)) {
        line.erase(std::remove_if(line.begin(), line.end(), [](char c) { return !std::isprint(c) && c != ' '; }), line.end());
        std::vector<std::string> tokens;
        boost::split(tokens, line, boost::is_any_of(","));
        if (tokens.size() == 2) {
            auto name = tokens[0];
            auto code = tokens[1];
            std::transform(code.begin(), code.end(), code.begin(), ::tolower);
            countriesMap_[name] = code;
        }
    }
    return true;
}

void Broadcast::reSetSessionIdInMessages(std::string oldSessionId, std::string newSessionId) {
    for (auto &conversation: conversations_) {
        conversation.second.setNewSesionId(oldSessionId, newSessionId);
    }
}

Wt::Json::Object Broadcast::createUserList() {
    Wt::Json::Array userList;
    for (const auto &connection: connections_) {
        Wt::Json::Object user;
        user["name"] = Wt::asString(connection->userName());
        user["age"] = connection->age();
        user["gender"] = Wt::asString(connection->gender());
        user["country"] = Wt::asString(connection->country());
        user["isoCountryCode"] = getCountryIsoCodeByCountry(connection->country());
        userList.push_back(user);
    }
    Wt::Json::Object data;
    data["data"] = userList;
    data["type"] = "userlist";
    data["count"] = (int)userList.size();
    return data;
}

void Broadcast::sendMessageQueueToSession(std::string receiverSessionId, std::string user1, std::string user2, std::vector<Message> messages) {
    Wt::Json::Array messagesJson;
    for (Broadcast::Message &message: messages) {
        messagesJson.push_back(message.json());
    }
    Wt::Json::Object broadcast{
        {"type", "messagequeue"},
        {"user1", Wt::Json::Value(user1)},
        {"user2", Wt::Json::Value(user2)},
        {"sessionid1", Wt::Json::Value(sessionIdForUserName(user1))},
        {"sessionid2", Wt::Json::Value(sessionIdForUserName(user2))},
        {"data", messagesJson}
    };
    addMessageToSessionBroadcast(receiverSessionId, broadcast);
}

void Broadcast::addMessageToSessionBroadcast(std::string sessionId, Wt::Json::Object message) {
    for (auto &connection: connections_) {
        if (connection->sessionId() == sessionId) {
            connection->addBroadcast(message);
        }
    }
}

void Broadcast::sendMessageCount(std::string sessionId) {
    int count{0};
    for (const auto &conversation: conversations_) {
        const auto key = conversation.first;
        const auto data = conversation.second;
        if ((key.compare(0, sessionId.length() + 1, sessionId + "/") == 0
                && !data.user1Read)
            || (key.compare(key.length() - sessionId.length() - 1, sessionId.length() + 1, "/" + sessionId) == 0
                && !data.user2Read)) {
            ++count;
        }
    }
    Wt::Json::Object broadcast{
        {"type", "unread-chats"},
        {"data", count}
    };
    addMessageToSessionBroadcast(sessionId, broadcast);
}

void Broadcast::sendBlockedMessage(std::string sessionId, std::string toUserName) {
    Wt::Json::Object broadcast{
        {"type", "system"},
        {"related-user", Wt::WString(toUserName)},
        {"data", "The user has blocked you."}
    };
    addMessageToSessionBroadcast(sessionId, broadcast);
}

void Broadcast::sendBlockDone(std::string sessionId, std::string toUserName) {
    addMessage(sessionId, toUserName, Message("Conversation is blocked."));
    Wt::Json::Object broadcast{
        {"type", "system"},
        {"related-user", Wt::WString(toUserName)},
        {"data", "blocked"}
    };
    addMessageToSessionBroadcast(sessionId, broadcast);

}

void Broadcast::sendUnblockDone(std::string sessionId, std::string toUserName) {
    addMessage(sessionId, toUserName, Message("Conversation is unblocked."));
    Wt::Json::Object broadcast{
        {"type", "system"},
        {"related-user", Wt::WString(toUserName)},
        {"data", "unblocked"}
    };
    addMessageToSessionBroadcast(sessionId, broadcast);
}

size_t Broadcast::WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) {
    size_t total_size = size * nmemb;
    output->append(reinterpret_cast<char*>(contents), total_size);
    return total_size;
}

Broadcast::Connection::Connection(const std::string &id, Client *client, const std::function<void ()> &fct):
        sessionId_(id),
        client_(client),
        fct_(fct) {
}

Client *Broadcast::Connection::client() const {
    return client_;
}

std::string Broadcast::Connection::sessionId() const {
    return sessionId_;
}

std::string Broadcast::Connection::userName() const {
    return client_->userName;
}

std::string Broadcast::Connection::country() const {
    return client_->country;
}

int Broadcast::Connection::age() const {
    return client_->age;
}

std::string Broadcast::Connection::gender() const {
    return client_->gender;
}

void Broadcast::Connection::setClient(Client *client) {
    client_ = client;
}

void Broadcast::Connection::addBroadcast(Wt::Json::Object broadcast) {
    broadcastFifo.push_back(broadcast);
}

std::list<Wt::Json::Object> Broadcast::Connection::getBroadcasts(bool clear) {
    auto broadcastCopy = broadcastFifo;
    if (clear) {
        broadcastFifo.clear();
    }
    return broadcastCopy;
}

bool Broadcast::Connection::setSessionId(std::string searchedSessionId, std::string newSessionId) {
    if (searchedSessionId == sessionId_) {
        sessionId_ = newSessionId;
        return true;
    }
    return false;
}

std::function<void ()> Broadcast::Connection::fct() {
    return fct_;
}


void Client::setActivity() {
    lastActivity_ = Wt::WDateTime::currentDateTime();
}

bool Client::activitiesTimedOut() {
    auto timeDifference = lastActivity_.secsTo(Wt::WDateTime::currentDateTime());
    return timeDifference > timeoutSeconds;
}

int Client::currentlyLoggedInSeconds() {
    auto currentDateTime = Wt::WDateTime::currentDateTime();
    return loginTimeStamp_.secsTo(currentDateTime);
}

Wt::Json::Object Client::json() {
    return Wt::Json::Object{
        {"name", Wt::WString(userName)},
        {"gender", Wt::WString(gender)},
        {"isoCountryCode", Wt::WString(isoCountryCode)},
        {"country", Wt::WString(country)},
        {"age", age}
    };
}

int Client::remainingSecondsToTimeout(){
    auto timeDifference = lastActivity_.secsTo(Wt::WDateTime::currentDateTime());
    return timeoutSeconds - timeDifference;
}

void Client::setLoggedIn() {
    loginTimeStamp_ = Wt::WDateTime::currentDateTime();
    lastActivity_ = loginTimeStamp_;
}

Broadcast::Message::Message(std::string fromSessionId_, Wt::WString message_):
        fromSessionId(fromSessionId_),
        sendType("text"),
        message(message_) {
    auto timestamp = std::time(nullptr);
    auto combinedString = std::to_string(timestamp) + fromSessionId_;
    Wt::Auth::SHA1HashFunction hashObj;
    auto hash = hashObj.compute(combinedString, "salt");
    messageId = Wt::WString(hash);
    sendTime = Wt::WDateTime::currentDateTime();
}

Broadcast::Message::Message(std::string fromSessionId_, std::shared_ptr<Magick::Blob> imageBlob):
        fromSessionId(fromSessionId_),
        sendType("image") {
    auto timestamp = std::time(nullptr);
    auto combinedString = std::to_string(timestamp) + fromSessionId_;
    Wt::Auth::SHA1HashFunction hashObj;
    auto hash = hashObj.compute(combinedString, "salt");
    messageId = Wt::WString(hash);
    sendTime = Wt::WDateTime::currentDateTime();
    Magick::Image image_;
    image_.read(*imageBlob);
    std::string imageData(reinterpret_cast<const char*>(imageBlob->data()), imageBlob->length());
    std::string imageFormat = image_.magick();
    image = {
        {"width", Wt::Json::Value((int)image_.columns())},
        {"height", Wt::Json::Value((int)image_.rows())},
        {"imageblobbase64", Wt::Json::Value(Wt::Utils::base64Encode(imageData))},
        {"type", Wt::Json::Value(imageFormat)}
    };
}

Broadcast::Message::Message(Wt::WString message_):
    systemMessage(true),
    sendType("text"),
    message (message_) {
    auto timestamp = std::time(nullptr);
    auto combinedString = std::to_string(timestamp) + "system09715";
    Wt::Auth::SHA1HashFunction hashObj;
    auto hash = hashObj.compute(combinedString, "salt");
    messageId = Wt::WString(hash);
    sendTime = Wt::WDateTime::currentDateTime();
}

Wt::Json::Object Broadcast::Message::json() {
    auto json = Wt::Json::Object();
    json["sender"] = Wt::Json::Value(fromSessionId);
    json["type"] = Wt::Json::Value(sendType);
    json["string"] = Wt::Json::Value(message);
    json["image"] = Wt::Json::Value(image);
    json["id"] = Wt::Json::Value(messageId);
    json["timestamp"] = sendTime.toString();
    return json;
}

void Broadcast::Message::setNewSesionId(std::string oldSessionId, std::string newSessionId) {
    if (fromSessionId == oldSessionId) {
        fromSessionId = newSessionId;
    }
}

void Broadcast::MessageQueue::setNewSesionId(std::string oldSessionId, std::string newSessionId) {
    for (auto &message: messages) {
        message.setNewSesionId(oldSessionId, newSessionId);
    }
}
