#include "yacreader_local_server.h"

#include <QLocalServer>
#include <QLocalSocket>
#include <QPointer>

#include "yacreader_global.h"
#include "db_helper.h"

#include "comic_db.h"

#include "QsLog.h"

using namespace YACReader;

QMutex YACReaderClientConnectionWorker::dbMutex;
// int YACReaderClientConnectionWorker::count = 0;
YACReaderLocalServer::YACReaderLocalServer(QObject *parent)
    : QObject(parent)
{
    localServer = new QLocalServer(this);
    QLocalServer::removeServer(YACREADERLIBRARY_GUID);
    if (!localServer->listen(YACREADERLIBRARY_GUID)) {
        QLOG_ERROR() << "Unable to create local server";
    }

    connect(localServer, &QLocalServer::newConnection, this, &YACReaderLocalServer::sendResponse);
}

bool YACReaderLocalServer::isListening()
{
    return localServer->isListening();
}

/*void YACReaderLocalServer::run()
{
        while(1)
                exec();
}*/

void YACReaderLocalServer::sendResponse()
{
    QLocalSocket *clientConnection = localServer->nextPendingConnection();
    // connect(clientConnection, SIGNAL(disconnected()),clientConnection, SLOT(deleteLater()));
    clientConnection->setParent(0);

    auto worker = new YACReaderClientConnectionWorker(clientConnection);
    if (worker != 0) {
        clientConnection->moveToThread(worker);
        connect(worker, &YACReaderClientConnectionWorker::comicUpdated, this, &YACReaderLocalServer::comicUpdated);
        connect(worker, &QThread::finished, worker, &QObject::deleteLater);
        worker->start();
    }

    QLOG_TRACE() << "connection incoming";
    // clientConnection->waitForBytesWritten();*/
    // clientConnection->disconnectFromServer();
}

bool YACReaderLocalServer::isRunning()
{
    QLocalSocket socket;
    socket.connectToServer(YACREADERLIBRARY_GUID);
    if (socket.waitForConnected(500))
        return true; // Server is running (another instance of YACReaderLibrary has been launched)
    return false;
}

void YACReaderLocalServer::close()
{
    localServer->close();
}

YACReaderClientConnectionWorker::YACReaderClientConnectionWorker(QLocalSocket *cc)
    : QThread(), clientConnection(cc)
{
}

YACReaderClientConnectionWorker::~YACReaderClientConnectionWorker()
{
}
/*#include <QFile>
#include <QTextStream>
#include <QDateTime>*/
void YACReaderClientConnectionWorker::run()
{
    /*{
        QFile f(QString("c:/temp/thread%1.txt").arg(count));
        f.open(QIODevice::Append);
        QTextStream out(&f);
        out << QString("Thread%1 starts").arg(count) << endl;
        f.close();
        }
        uint t1 = QDateTime::currentMSecsSinceEpoch();*/

    quint64 libraryId;
    ComicDB comic;
    OpenComicSource source = { OpenComicSource::ReadingList, 0 };
    qulonglong nextComicId;
    int tries = 0;
    int dataAvailable = 0;
    QByteArray packageSize;
    clientConnection->waitForReadyRead(1000);
    while (((long unsigned int)packageSize.size() < sizeof(quint32)) && (tries < 20)) {
        packageSize.append(clientConnection->read(sizeof(quint32) - packageSize.size()));
        clientConnection->waitForReadyRead(100);
        if (dataAvailable == packageSize.size()) {
            tries++;
        }
        dataAvailable = packageSize.size();
    }
    if (tries == 20) {
        QLOG_ERROR() << "Local connection: unable to read the message size" << clientConnection->errorString();
        return;
    }

    QDataStream sizeStream(packageSize);
    sizeStream.setVersion(QDataStream::Qt_4_8);
    quint32 totalSize = 0;
    sizeStream >> totalSize;

    tries = 0;
    QByteArray data;
    int dataRead = 0;
    while ((quint32)data.size() < totalSize && tries < 200) {
        data.append(clientConnection->readAll());
        if ((quint32)data.length() < totalSize)
            clientConnection->waitForReadyRead(100);
        if (dataRead == data.length()) // no bytes were read
            tries++;
        dataRead = data.length();
    }
    if (tries == 200) {
        QLOG_ERROR() << QString("Local connection: unable to read message (%1,%2)").arg(data.size()).arg(totalSize);
        return;
    }
    QDataStream dataStream(data);
    quint8 msgType;
    dataStream >> msgType;

    switch (msgType) {
    case YACReader::RequestComicInfo: {
        dataStream >> libraryId;
        dataStream >> source;
        dataStream >> comic;

        QList<ComicDB> siblings;

        if (source.source == OpenComicSource::ReadingList) {
            getComicInfoFromReadingList(libraryId, source.sourceId, comic, siblings);
        } else {
            getComicInfo(libraryId, comic, siblings);
        }

        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
        out.setVersion(QDataStream::Qt_4_8);
        out << (quint32)0;
        out << comic;
        out << siblings;
        out.device()->seek(0);
        out << (quint32)(block.size() - sizeof(quint32));

        int written = 0;
        tries = 0;
        while (written != block.size() && tries < 200) {
            int ret = clientConnection->write(block);
            clientConnection->waitForBytesWritten(10);
            if (ret != -1) {
                written += ret;
                clientConnection->flush();
            } else
                tries++;
        }
        if (tries == 200 && written != block.size()) {
            QLOG_ERROR() << QString("Local connection (comic info requested): unable to send response (%1,%2)").arg(written).arg(block.size());
        }
        break;
    }
    case YACReader::SendComicInfo: {
        bool nextComicInfoAvailable;

        dataStream >> libraryId;
        dataStream >> comic;

        if (dataStream.atEnd()) {
            nextComicInfoAvailable = false;
        } else {
            nextComicInfoAvailable = true;
            dataStream >> nextComicId;
        }

        if (nextComicInfoAvailable) {
            updateComic(libraryId, comic, nextComicId);
        } else {
            updateComic(libraryId, comic);
        }
        break;
    }
    }

    clientConnection->waitForDisconnected();
    clientConnection->deleteLater();
    /*count++;
        uint t2 = QDateTime::currentMSecsSinceEpoch();
        {
        QFile f(QString("c:/temp/thread%1.txt").arg(count));
        f.open(QIODevice::Append);
        QTextStream out(&f);
        out << QString("Thread%1 ends : time - %2").arg(count).arg(t2-t1) << endl;
        f.close();
        }*/
}

void YACReaderClientConnectionWorker::getComicInfo(quint64 libraryId, ComicDB &comic, QList<ComicDB> &siblings)
{
    QMutexLocker locker(&dbMutex);
    comic = DBHelper::getComicInfo(libraryId, comic.id);
    siblings = DBHelper::getSiblings(libraryId, comic.parentId);
}

void YACReaderClientConnectionWorker::getComicInfoFromReadingList(quint64 libraryId, unsigned long long readingListId, ComicDB &comic, QList<ComicDB> &siblings)
{
    QMutexLocker locker(&dbMutex);
    comic = DBHelper::getComicInfo(libraryId, comic.id);
    siblings = DBHelper::getReadingListFullContent(libraryId, readingListId, true);
}

void YACReaderClientConnectionWorker::updateComic(quint64 libraryId, ComicDB &comic)
{
    QMutexLocker locker(&dbMutex);
    DBHelper::update(libraryId, comic.info);
    emit comicUpdated(libraryId, comic);
}

void YACReaderClientConnectionWorker::updateComic(quint64 libraryId, ComicDB &comic, qulonglong nextComicId)
{
    QMutexLocker locker(&dbMutex);
    DBHelper::update(libraryId, comic.info);
    ComicInfo nextcomicinfo;
    nextcomicinfo.id = nextComicId;
    DBHelper::setComicAsReading(libraryId, nextcomicinfo);

    emit comicUpdated(libraryId, comic);
}
