/****************************************************************************
 ** Copyright (C) 2008-2013 Grigory A. Mozhaev <zcrendel@gmail.com>
 **
 ** This file is part of QMultiRecord (http://qt-apps.org/content/show.php?content=106254).
 **
 ** This file may be used under the terms of the GNU General Public
 ** License version 2.0 as published by the Free Software Foundation
 ** and appearing in the file LICENSE.GPL included in the packaging of
 ** this file.  Please review the following information to ensure GNU
 ** General Public Licensing requirements will be met:
 ** http://www.trolltech.com/products/qt/opensource.html
 **
 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ****************************************************************************/
//! -*- coding: UTF-8 -*-
#define SOURCE_CODING "UTF-8"

#include "generatethread.h"

#include <QtCore/QCryptographicHash>

class GenerateThreadPrivate {
public:
    QCryptographicHash *m_hash;
    QMap<QString, int> m_jobMap;
    QMap<QString, QString> m_fullNames;

    bool m_initialized;

    quint64 m_percent;

    GenerateThreadPrivate() {
        m_initialized = false;
        m_hash = new QCryptographicHash(QCryptographicHash::Md5);
    }
    ~GenerateThreadPrivate() {
        if (m_hash) {
            delete m_hash;
            m_hash = 0;
        }
    }
};

GenerateThread::GenerateThread() {
    m_data = new GenerateThreadPrivate;

    Q_ASSERT(m_data!=0);
}

GenerateThread::~GenerateThread() {
    if (m_data) {
        delete m_data;
        m_data = 0;
    }
}


void GenerateThread::init(const QStringList &jobList, const QStringList &fullNames) {
    if (m_data) {
        for (int i=0; i < jobList.count(); i++) {
            m_data->m_jobMap.insert(jobList.at(i), 1);            
            m_data->m_fullNames.insert(jobList.at(i), fullNames.at(i));
        }

        m_data->m_initialized = true;
    }
}

void GenerateThread::stop() {
    if (m_data) {
        m_mutex.lock();
        QMap<QString, int>::Iterator currentJob = m_data->m_jobMap.begin();
        while (currentJob != m_data->m_jobMap.end()) {
            if (currentJob.value() == 1)
                currentJob.value() = 0;
            ++currentJob;
        }
        m_mutex.unlock();
    }
}

void GenerateThread::run() {
    qDebug() << "CGenerateThread::run()";

    if (!m_data->m_initialized) {
        qDebug() << "CGenerateThread not initialized! exiting...";
        return;
    }

    QMap<QString, int>::Iterator currentJob = m_data->m_jobMap.begin();
    QString prevJob = QString::null; 
    while (currentJob != m_data->m_jobMap.end()) {
        switchJob(prevJob, currentJob.key());

        QFile file(m_data->m_fullNames.value(currentJob.key()));
        if (currentJob.value() == 1) {
            if (!file.open(QIODevice::ReadOnly)) {
                m_mutex.lock();
                currentJob.value() = 2;
                m_mutex.unlock();
                progress(currentJob.key(), 0);
                prevJob = currentJob.key();
                ++currentJob;
                continue;
            }
        } else {
            progress(currentJob.key(), 0, tr("CANCELED"));
            switchJob(prevJob, currentJob.key());
            prevJob = currentJob.key();
            ++currentJob;
            continue ;
        }

        progress(currentJob.key(), 0);

        // calculate hash
        m_data->m_hash->reset();

        QFileInfo fileInfo(m_data->m_fullNames.value(currentJob.key()));
        quint64   percent  = fileInfo.size() / 100;
        quint64   red      = 0;
        int       tryCount = 0;
        bool      error    = false;
        int       currentPercent = 0;
        int       prevPercent    = 0;

        // ******************
        while (!file.atEnd()) {
            if (currentJob.value() == 0) {
                switchJob(prevJob, currentJob.key());
                progress(currentJob.key(), 0, tr("CANCELED"));
                m_mutex.lock();
                currentJob.value() = 0;
                m_mutex.unlock();
                break;
            }

            char buffer[16384];
            qint64 retVal = file.read(buffer, 16384);
            if (retVal > 0) {
                tryCount = 0;
                red += retVal;
                m_data->m_hash->addData(buffer, retVal);
                currentPercent = red / percent;
                if (currentPercent != prevPercent) {
                    progress(currentJob.key(), currentPercent, tr("progress..."));
                    prevPercent = currentPercent;
                    qApp->processEvents();
                }
            } else
                if (retVal < 0) {
                    tryCount++;
                    if (tryCount > 2) {
                        error = true;
                        progress(currentJob.key(), 0, tr("ERROR"));
                        m_mutex.lock();
                        currentJob.value() = 4;
                        m_mutex.unlock();
                        qDebug() << "Error: Can't read from device";
                        qDebug() << "Current md5sum is:" << 
                            m_data->m_hash->result().toHex();
                        break;
                    }
                } else {
                    break;
                }
        }
        file.close();
        // ******************

        if ((!error) && (currentJob.value()==1)) {
            qDebug() << "DONE!";
            QFile md5File(QString("%1.md5sum").arg(m_data->m_fullNames.value(currentJob.key())));
            if (md5File.open(QIODevice::WriteOnly)) {
                QFileInfo md5FileInfo(m_data->m_fullNames.value(currentJob.key()));
                QTextStream out(&md5File);
                out << m_data->m_hash->result().toHex() << QLatin1String("  ") << md5FileInfo.fileName() << endl;
                md5File.close();
                progress(currentJob.key(), 100, tr("DONE"));
                m_mutex.lock();
                currentJob.value() = 3;
                m_mutex.unlock();
            } else {
                error = true;
                progress(currentJob.key(), 0, tr("ERROR"));
                m_mutex.lock();
                currentJob.value() = 4;
                m_mutex.unlock();
            }
        }

        prevJob = currentJob.key();
        ++currentJob;
    }
    switchJob(prevJob, QString::null);

    qApp->processEvents();
}

int GenerateThread::jobStatus(const QString &jobName) {
    if ((m_data) && (m_data->m_jobMap.contains(jobName)))
        return m_data->m_jobMap.value(jobName);

    return 0;
}

void GenerateThread::cancelJob(const QString &jobName) {
    m_mutex.lock();
    m_data->m_jobMap.insert(jobName, 0);
    m_mutex.unlock();
}
