/****************************************************************************
 ** 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 "dvdmodel.h"

#include <QtCore/QCryptographicHash>

class CheckThreadPrivate {
public:
    QString m_device;
    bool    m_success;
    bool    m_initialized;
    QCryptographicHash *m_hash;
    quint64 m_percent;

    DvdModel *m_model;
    bool m_exit;

    CheckThreadPrivate() {
        m_device = QString::null;
        m_initialized = false;        
        m_hash = new QCryptographicHash(QCryptographicHash::Md5);
        m_success = false;

        m_model = 0;
        m_exit  = false;
    }
    ~CheckThreadPrivate() {
        if (m_hash) {
            delete m_hash;
            m_hash = 0;
        }
    }
};

CheckThread::CheckThread() {
    m_data = new CheckThreadPrivate;

    Q_ASSERT(m_data!=0);
}

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


void CheckThread::init(DvdModel *model) {
    if (!model)
        return ;

    if (model->deviceFile().isNull())
        return ;

    if (m_data) {
        m_data->m_model = model;

        m_data->m_device = model->deviceFile();
        m_data->m_percent = model->capacity() / 100;

        m_data->m_initialized = true;
    }
}

void CheckThread::stop() {
    if (m_data)
        m_data->m_exit = true;
}

void CheckThread::run() {
    if (!m_data->m_initialized) {
        m_data->m_success = false;
        return;
    }

    sleep(2);

    QString program;
    QStringList arguments;
    QProcess process;

    program = QLatin1String("eject");
    arguments << m_data->m_device;
    arguments << QLatin1String("-t");
    process.start(program, arguments);
    if (process.waitForStarted()) {
        while ((!process.waitForFinished(1000)) &&
               (process.state()==QProcess::Running)) {       
        }
    }

    int counter = 0;
    while (true) {
        if (m_data->m_model->dvdStatus() == DvdModel::dsDataCd)
            break;

        if (m_data->m_model->dvdStatus() == DvdModel::dsDataDvd)
            break;

        if (m_data->m_model->dvdStatus() == DvdModel::dsDataBd)
            break;

        if (counter >= 25) {
            m_data->m_success = false;
            emit(progress(100));
            return ;
        }

        counter++;
        sleep(1);
    }

    //mpData->mpHash->reset();
    progress(0);

    qDebug() << "Open device " << m_data->m_device << "for reading...";
    QFile file(m_data->m_device);
    if (!file.open(QIODevice::ReadOnly)) {
        m_data->m_success = false;
        qDebug() << "Failed!";
        emit(progress(100));
        return;
    }
    qDebug() << "DONE";

    quint64 red = 0;
    quint64 isoSize = m_data->m_model->isoSize();
    int tryCount = 0;
    bool error = false;

    qDebug() << "ISO file size to check:" << isoSize;

    //while (!file.atEnd()) {
    while (1) {
        if (m_data->m_exit) {
            m_data->m_success = false;
            break;
        }

        char buffer[4096];
        qint64 retVal = file.read(buffer, 4096);
        if (retVal > 0) {
            tryCount=0;
            red += retVal;

            if (isoSize > 0) {
                if (isoSize == red) {
                    qDebug() << "Read from media equal ISO file size - quit from thread";
                    m_data->m_hash->addData(buffer, retVal);
                    break;
                } else
                if (red > isoSize) {
                    qDebug() << "Media Size is Greater than ISO size!";
                    qDebug() << "Read from media:" << red;
                    qDebug() << "ISO file size:" << isoSize;

                    retVal -= (red - isoSize);

                    if (retVal > 0) {
                        qDebug() << "add last bytes:" << retVal;
                        m_data->m_hash->addData(buffer, retVal);
                    }

                    break;
                }
            }

            m_data->m_hash->addData(buffer, retVal);
            emit(progress(red / m_data->m_percent));
        } else
        if (retVal < 0) {
            usleep(500000);
            tryCount++;
            if (tryCount > 2) {
                error = true;
                qDebug() << "Error: Can't read from device";
                qDebug() << "Current md5sum is:" << 
                    m_data->m_hash->result().toHex();
                break;
            }
        } else {
            break;
        }
    }
    file.close();
    emit(progress(100));

    qDebug() << "Check Sum Ended";

    qApp->processEvents();

    program = QLatin1String("eject");
    arguments.clear();
    arguments << m_data->m_device;

    process.start(program, arguments);
    if (process.waitForStarted()) {
        while ((!process.waitForFinished(500)) &&
               (process.state()==QProcess::Running)) {
        }
    }

    if ((!m_data->m_exit) && (!error)) {
        m_data->m_success = true;
    } else {
        m_data->m_success = false;
    }

    qDebug() << "CD ejected";
}

bool CheckThread::done() {
    return m_data->m_success;
}

QString CheckThread::hash() {
    if (m_data) {
        if (m_data->m_hash) {
            return m_data->m_hash->result().toHex();
        }
    }

    return QString();
}
