/** dirman.cpp - Manage the images in a directory
 *
 * bsAniPic - http://www.buckosoft.com/bsAniPic/
 *
 * Copyright(c) BuckoSoft, 2017.
 * Released under GNU GPL v3 https://www.gnu.org/licenses/gpl-3.0.en.html
 */

#include <QDir>
#include <QDebug>
#include <QThread>
#include <QImage>
#include <QTimer>

#include "dirman.h"

static const bool DEBUG = false;

static QString bsAniPicDirName = ".bsAniPic";
static QString bsAniPicDebugStore = "debugStore.txt";
static QString configFileName = "config.txt";

DirMan::DirMan(QObject *parent) : QObject(parent)
{
    currentImage = -1;
    isBsacDirectory = false;
    isBsAniDirectory = false;
    startImageReader();
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(timerFired()));
    timer->start(100);

}

DirMan::~DirMan()
{
    delete imageReader;
}

void DirMan::setLoadBsacText(bool onoff)
{
    isLoadBsacText = onoff;
    imageReader->config(directory, isBsacDirectory, isLoadBsacText);
}

void DirMan::setDirectory(QString newDirectory)
{
    QVector<DebugText>  debugTextList;
    if (newDirectory != NULL)
        this->directory = newDirectory;
    if (!directory.endsWith("/"))
        directory += "/";

    povDebugFileName.clear();

    QString t = this->directory + "/" + "BSAC";
    QDir tDir(t);
    if (tDir.exists()) {
        imageDir = t + "/frames/";
        textDir = t + "/texts/";
        isBsacDirectory = true;
        isBsAniDirectory = false;
    } else {
        imageDir = directory + "/";
        isBsacDirectory = false;
    }
    if (!isBsacDirectory) {
        t = this->directory + bsAniPicDirName;
        QDir tDir(t);
        if (tDir.exists()) {
            isBsAniDirectory = true;
            bsAniPicDir = tDir;
            QFile f(tDir.filePath(configFileName));
            if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
                QByteArray line = f.readAll();
                QString s = line;
                povDebugFileName = s.trimmed();
                qDebug() << "got povDebugFileName: " << povDebugFileName;
            }
        }
    }
    if (isBsAniDirectory) {
        t = this->directory + bsAniPicDirName + "/" + bsAniPicDebugStore;
        readDebugText(debugTextList, t);
    }
    QDir dir(imageDir);
    QStringList filters;
    filters << "*.png";
    dir.setNameFilters(filters);
    qInfo() << "Images to process:" << dir.count();
    for (int i=0; i<imageList.size(); i++) {
        if (imageList.at(i) != NULL)
            delete imageList.at(i);
    }
    imageList.resize(dir.count());
    imageList.clear();
    imagesInMemoryList.clear();

    myGeneration = 0;
    currentImage = -1;
    QStringList dlist = dir.entryList(filters);
    for (int i=0; i<dlist.count(); i++) {
        QString s = dlist.at(i);
        BsImage* bi = new BsImage();
        bi->filename = s;
        bi->frame = extractFrameNumber(s);
        imageList.append(bi);
    }
    imageReader->config(directory, isBsacDirectory, isLoadBsacText);

    if (isBsAniDirectory) {
       t = this->directory + povDebugFileName;
       readDebugText(debugTextList, t);
       for (int i=0; i<debugTextList.length(); i++) {
           const DebugText& dt = debugTextList[i];
           for (int j=0; j<imageList.length(); j++) {
               if (imageList[j]->frame == dt.frame) {
                   imageList[j]->text = dt.text;
                   break;
               }
           }
       }
       // now cache the debug
       t = this->directory + bsAniPicDirName + "/" + bsAniPicDebugStore;
       QFile::rename(t, t+"~");
       QFile file(t);
       if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
           qWarning() << "Can't open for writing: " << t;
       } else {
           QTextStream out(&file);
           foreach(BsImage* bi, imageList) {
               if (!bi->text.isEmpty()) {
                   out << "@@F:" << bi->frame << "\n";
                   out << bi->text;
                   out << "@@end\n";
               }
           }
           file.close();
       }
    }
}

void    DirMan::createBsAniDirectory(QString debugName)
{
    QString t = this->directory + bsAniPicDirName;
    QDir tDir(this->directory);
    if (!tDir.exists(bsAniPicDirName)) {
        tDir.mkdir(bsAniPicDirName);
    }
    t += "/" + configFileName;
    QFile f(t);
    if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qInfo() << "Can't open for writing: " << t;
        return;
    }
    QTextStream out(&f);
    out << debugName;
    f.close();
}

void    DirMan::refreshDirectory()
{
    BsImage* bi = imageList[currentImage];
    QString oldName = bi->filename;
    readerQueueMutex.lock();
    readerQueue.clear();
    readerQueueMutex.unlock();
    //readerQueueWaitCondition.notify_all();
    setDirectory();
    int i;
    bool match = false;
    for (i=0; i<imageList.size(); i++) {
        if (imageList[i]->filename == oldName) {
            match = true;
            break;
        }
    }
    if (!match) {
        for (i=0; i<imageList.size(); i++) {
            if (imageList[i]->filename >= oldName) {
                if (i > 0)
                    i--;
                match = true;
                break;
            }
        }
        if (!match) {
            if (imageList.size() > currentImage && currentImage > -1) {
                i = currentImage;
                match = true;
            }
        }
    }
    setCurrentImage(match == true ? i : 0);
}

void     DirMan::setCurrentImage(int index)
{
    if (imageList.isEmpty())
        return;
    if (DEBUG) qDebug() << "DirMan:setCurrentImage(" << index << ")";
    BsImage* bi;
    currentImage = index;
    bi = imageList.at(currentImage);
    if (bi->image == NULL) {
        readImage(currentImage);
    }
}

const BsImage* DirMan::getCurrentBsImage()
{
    if (imageList.isEmpty())
        return(NULL);
    if (DEBUG) qDebug() << "DirMan:getCurrentBsImage(" << currentImage << ")";
    int timeout = 500;   // 500 ms
    if (currentImage == -1)
        currentImage = 0;
    while (timeout-- >= 0) {
        BsImage* bi = imageList.at(currentImage);
        if (bi->image != NULL) {
            if (currentImage < imageList.size()-1
             && imageList[currentImage+1]->image == NULL) {
                readImage(currentImage+1);
            }
            return(bi);
        }
        QThread::msleep(1);
    }
    return(NULL);
}

const BsImage* DirMan::getCurrentBsImageNaked()
{
    if (imageList.isEmpty())
        return(NULL);
    if (currentImage == -1)
        currentImage = 0;
    return(imageList[currentImage]);
}

const BsImage* DirMan::getBsImageNaked(int index)
{
    if (imageList.isEmpty())
        return(NULL);
    return(imageList[index]);
}

const QImage* DirMan::getCurrentImage()
{
    if (DEBUG) qDebug() << "DirMan:getCurrentImage(" << currentImage << ")";
    const int sleepDelay = 5;
    int maxWait = 1000/sleepDelay;
    while (true) {
        BsImage* bi = imageList.at(currentImage);
        if (bi->image != NULL)
            return(bi->image);
        if (maxWait-- <= 0) {
            return(getBadImage());
        }
        QThread::msleep(5);
    }
    return(NULL);
}

void DirMan::readImage(int which)
{
    if (DEBUG) qDebug() << "DirMan::readImage(" << which << ")";
    BsImage* bi = imageList[which];
    readerQueueMutex.lock();
    if (!readerQueue.contains(bi))
        readerQueue.append(bi);
    readerQueueMutex.unlock();
    readerQueueWaitCondition.notify_all();
    if (!imagesInMemoryList.contains(bi))
        imagesInMemoryList.append(bi);
}

void DirMan::startImageReader()
{
    imageReader = new ImageReader();
    imageReader->setObjectName("ImageReader");
    imageReader->setUp(&readerQueue, &readerQueueMutex, &readerQueueWaitCondition);
    imageReader->start();
}

void DirMan::timerFired()
{
    while (imagesInMemoryList.size() > NumImagesToHold) {
        BsImage* bi = imagesInMemoryList.first();
        if (bi->image != NULL) {
            delete bi->image;
            bi->image = NULL;
            //qDebug() << "delete image: (" << imagesInMemoryList.size() << ") " << bi->filename;
            //qDebug() << "in memory" << imagesInMemoryList[0]->filename << imagesInMemoryList[1]->filename<< imagesInMemoryList[2]->filename
            //            << imagesInMemoryList[3]->filename<< imagesInMemoryList[4]->filename;
        }
        imagesInMemoryList.removeFirst();
    }
}

int DirMan::getImageNumberFromFrameNumber(int frameNumber)
{
    QString fn = imageList.first()->filename;
    int i = fn.indexOf('.');
    QString ext = fn.mid(i);
    fn = fn.left(i);
    //qDebug() << "fn=" << fn;
    for (i=fn.length()-1; i>=0; i--)
        if (!fn.at(i).isDigit())
            break;
    int digits = fn.length()-i-1;
    //qDebug() << "digits=" << digits;
    fn = fn.left(i+1) + QString("%1").arg(frameNumber, digits, 10, QLatin1Char('0')) + ext;
    //qDebug() << "fn:" << fn;
    for (i=0; i<imageList.size(); i++) {
        if (imageList[i]->filename == fn) {
            return(i);
        }
    }
    for (i=0; i<imageList.size(); i++) {
        if (imageList[i]->filename > fn) {
            if (i > 0)
                return(i-1);
            else
                return(0);
        }
    }
    return(imageList.size()-1);
}

void DirMan::prepareToDie()
{
    timer->stop();
    readerQueueMutex.lock();
    readerQueue.clear();
    readerQueueMutex.unlock();
    readerQueueWaitCondition.notify_all();

    imageReader->setDie();
    readerQueueWaitCondition.wakeAll();
    imageReader->wait(100);

}

void DirMan::readDebugText(QVector<DebugText>& textList, QString& fileName)
{
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "Can't open debug file: " << fileName;
        return;
    }
    QTextStream in(&file);
    QString frameText = "";
    int     frameNumber = 0;
    bool inFrame = false;
    while (!in.atEnd()) {
        QString line = in.readLine();
        if (inFrame) {
            if (line.startsWith("@@end")) {
                bool replaced = false;
                for (int i=0; i<textList.length(); i++) {
                    if (textList[i].frame == frameNumber) {
                        textList[i].text = frameText;
                        replaced = true;
                        break;
                    }
                }
                if (!replaced)
                    textList.append(DebugText(frameNumber, frameText));
                frameText = "";
                inFrame = false;
                continue;
            }
            frameText += line;
            frameText += "\n";
        } else {
            if (line.startsWith("@@F:")) {
                QString s = line.mid(4);
                int i = s.toInt();
                if (i != 0) {
                    frameNumber = i;
                    inFrame = true;
                }
                continue;
            }
        }
    }
}

int extractFrameNumber(const QString& f)
{
    QString s = f.left(f.lastIndexOf('.'));
    for (int i=s.length()-1; i>=0; i--) {
        if (!s[i].isDigit()) {
            return(s.mid(i+1).toInt());
        }
    }
    return(0);
}
