/*
 * This is the source code of tgnet library v. 1.0
 * It is licensed under GNU GPL v. 2 or later.
 * You should have received a copy of the license in this archive (see LICENSE).
 *
 * Copyright Nikolai Kudashov, 2015.
 */

#include <algorithm>
#include <openssl/rand.h>
#include "ConnectionSession.h"
#include "MTProtoScheme.h"
#include "ConnectionsManager.h"
#include "NativeByteBuffer.h"

void ConnectionSession::recreateSession() {
    processedMessageIds.clear();
    messagesIdsForConfirmation.clear();
    processedSessionChanges.clear();
    nextSeqNo = 0;

    genereateNewSessionId();
}

void ConnectionSession::genereateNewSessionId() {
    int64_t newSessionId;
    RAND_bytes((uint8_t *) &newSessionId, 8);
#if USE_DEBUG_SESSION
    sessionId = (0xabcd000000000000L | (newSessionId & 0x0000ffffffffffffL));
#else
    sessionId = newSessionId;
#endif
}

void ConnectionSession::setSessionId(int64_t id) {
    sessionId = id;
}

int64_t ConnectionSession::getSissionId() {
    return sessionId;
}

uint32_t ConnectionSession::generateMessageSeqNo(bool increment) {
    uint32_t value = nextSeqNo;
    if (increment) {
        nextSeqNo++;
    }
    return value * 2 + (increment ? 1 : 0);
}

bool ConnectionSession::isMessageIdProcessed(int64_t messageId) {
    return std::find(processedMessageIds.begin(), processedMessageIds.end(), messageId) != processedMessageIds.end();
}

void ConnectionSession::addProcessedMessageId(int64_t messageId) {
    if (processedMessageIds.size() > 300) {
        processedMessageIds.erase(processedMessageIds.begin(), processedMessageIds.begin() + 100);
    }
    processedMessageIds.push_back(messageId);
}

bool ConnectionSession::hasMessagesToConfirm() {
    return !messagesIdsForConfirmation.empty();
}

void ConnectionSession::addMessageToConfirm(int64_t messageId) {
    if (std::find(processedMessageIds.begin(), processedMessageIds.end(), messageId) != processedMessageIds.end()) {
        return;
    }
    messagesIdsForConfirmation.push_back(messageId);
}

NetworkMessage *ConnectionSession::generateConfirmationRequest() {
    NetworkMessage *networkMessage = nullptr;

    if (!messagesIdsForConfirmation.empty()) {
        TL_msgs_ack *msgAck = new TL_msgs_ack();
        msgAck->msg_ids.insert(msgAck->msg_ids.begin(), messagesIdsForConfirmation.begin(), messagesIdsForConfirmation.end());
        NativeByteBuffer *os = new NativeByteBuffer(true);
        msgAck->serializeToStream(os);
        networkMessage = new NetworkMessage();
        networkMessage->message = std::unique_ptr<TL_message>(new TL_message);
        networkMessage->message->msg_id = ConnectionsManager::getInstance().generateMessageId();
        networkMessage->message->seqno = generateMessageSeqNo(false);
        networkMessage->message->bytes = os->capacity();
        networkMessage->message->body = std::unique_ptr<TLObject>(msgAck);
        messagesIdsForConfirmation.clear();
    }

    return networkMessage;
}

bool ConnectionSession::isSessionProcessed(int64_t sessionId) {
    return std::find(processedSessionChanges.begin(), processedSessionChanges.end(), sessionId) != processedSessionChanges.end();
}

void ConnectionSession::addProcessedSession(int64_t sessionId) {
    processedSessionChanges.push_back(sessionId);
}
