/****************************************************************************
 ** 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 <QtCore>
#include <QtGui>

#include "application.h"

#include "dvdview.h"

static QMap<QString,QIcon> m_iconMap;

QPixmap DvdView::icon(const QString &name) {
    if (name.isNull())
        return QPixmap();

    if (m_iconMap.contains(name)) {
        return m_iconMap.value(name).pixmap(46,46);
    }

    QPixmap tmp(QFile::decodeName(":/icons/%1.png").arg(name));

    if (!tmp.isNull()) {
        QIcon icon(tmp);
        m_iconMap.insert(name, icon);
        return icon.pixmap(46,46);
    }

    return QPixmap();
}


DvdView::DvdView(QWidget *parent, Qt::WindowFlags f)
    : QFrame(parent, f)
    , m_cdSpeedIndex(-1)
    , m_dvdSpeedIndex(-1)
    , m_bdSpeedIndex(-1)
    , m_isoSize(0)
    , m_animateFlag(false)
    , m_model(0)
    , m_timer(0)
    , m_menu(0)
{
    setupUi(this);

    wideStatusLabel->setText(QLatin1String(""));
    iconLabel->setText(QLatin1String(""));
    progressBar->setValue(0);
    hashLabel1->setText(QLatin1String(""));
    hashLabel2->setText(QLatin1String(""));
    cancelButton->setEnabled(false);
/*
    m_model = new DvdModel();
    if (m_model) {
        connect(m_model, SIGNAL(statusChanged(const QString &, const DvdModel::DeviceType  &,
                                              const DvdModel::DvdStatus   &,
                                              const DvdModel::ProcessMode &,
                                              quint64,
                                              const QString &)),
                this, SLOT(statusChanged(const QString &, const DvdModel::DeviceType  &,
                                        const DvdModel::DvdStatus   &,
                                        const DvdModel::ProcessMode &,
                                        quint64,
                                        const QString &)));

        connect (m_model, SIGNAL(progress(int,const QString &)),
                 this, SLOT(progress(int,const QString &)));
    }
*/
    connect(Application::instance(), SIGNAL(pathsOutdated()), this, SLOT(refreshRootDirsSlot()));
}

DvdView::~DvdView() {
    if (m_menu)
        delete m_menu;

    if (m_model)
        delete m_model;
}

void DvdView::setModel(DvdModel *model) {
    if (m_model) {
        disconnect(m_model, SIGNAL(statusChanged(const QString &, const DvdModel::DeviceType  &,
                                              const DvdModel::DvdStatus   &,
                                              const DvdModel::ProcessMode &,
                                              quint64,
                                              const QString &)),
                this, SLOT(statusChanged(const QString &, const DvdModel::DeviceType  &,
                                        const DvdModel::DvdStatus   &,
                                        const DvdModel::ProcessMode &,
                                        quint64,
                                        const QString &)));

        disconnect (m_model, SIGNAL(progress(int,const QString &)),
                 this, SLOT(progress(int,const QString &)));
    }

    m_model = model;

    connect(m_model, SIGNAL(statusChanged(const QString &, const DvdModel::DeviceType  &,
                                          const DvdModel::DvdStatus   &,
                                          const DvdModel::ProcessMode &,
                                          quint64,
                                          const QString &)),
            this, SLOT(statusChanged(const QString &, const DvdModel::DeviceType  &,
                                    const DvdModel::DvdStatus   &,
                                    const DvdModel::ProcessMode &,
                                    quint64,
                                    const QString &)));

    connect (m_model, SIGNAL(progress(int,const QString &)),
             this, SLOT(progress(int,const QString &)));
}

DvdModel *DvdView::model() {
    return m_model;
}

/*
void DvdView::setDevice(const QString& device, const QString& deviceInfo) {
    if (m_model) {
        m_model->setDeviceFile(device);
        m_model->setDeviceInfo(deviceInfo);
    }
}
*/

void DvdView::setLabel(const QString& label) {
    numberLabel->setText(label);
}

void DvdView::statusChanged(const QString &device, 
                             const DvdModel::DeviceType  &deviceType,
                             const DvdModel::DvdStatus   &dvdStatus,
                             const DvdModel::ProcessMode &processMode,
                             quint64 capacity,
                             const QString &hash) {
    //qDebug() << "DvdView::statusChanged " << device << deviceType << dvdStatus << processMode << capacity << hash;
    deviceLabel->setText(device);
    hashLabel2->setText(hash);
    blankDataLabel->setText("");
    wideStatusLabel->setText("");
    taskStatusLabel->setText("");
    taskStatusLabel->setEnabled(true);

    switch (deviceType) {

    case DvdModel::dtCdWriter: {
        if ((processMode!=DvdModel::pmWriting) &&
                (processMode!=DvdModel::pmChecking))
            iconLabel->setPixmap(icon(QLatin1String("cdrom")));
        break;
    }
    case DvdModel::dtDvdWriter: {
        if ((processMode!=DvdModel::pmWriting) &&
                (processMode!=DvdModel::pmChecking))
            iconLabel->setPixmap(icon(QLatin1String("dvdrom")));
        break;
    }
    case DvdModel::dtBdWriter: {
        if ((processMode!=DvdModel::pmWriting) &&
                (processMode!=DvdModel::pmChecking))
            iconLabel->setPixmap(icon(QLatin1String("blueray")));
        break;
    }

    case DvdModel::dtUndefined:
    case DvdModel::dtUnsupported:
    default:  {
        disableGui();
        return ;
    }

    };

    switch (dvdStatus) {

    case DvdModel::dsUndefined : {
        speedCombo->clear();
        m_speedList = 0;

        statusLabel->setText("Undefined");
        disableWrite();
        disableCheck();
        break;
    }

    case DvdModel::dsNoDisc : {
        speedCombo->clear();
        m_speedList = 0;

        statusLabel->setText("No disc");
        disableWrite();
        disableCheck();
        break;
    }

    case DvdModel::dsEmptyCd : {
        blankDataLabel->setText("BLANK");
        statusLabel->setText("CD-R(W)");

        if (m_speedList != 1) {
            speedCombo->clear();
            speedCombo->addItem("4");
            speedCombo->addItem("8");
            speedCombo->addItem("12");
            speedCombo->addItem("16");
            speedCombo->addItem("24");
            speedCombo->addItem("32");
            speedCombo->addItem("48");
            speedCombo->addItem("52");

            if (m_cdSpeedIndex < 0)
                m_cdSpeedIndex = 4;

            m_speedList = 1;
            speedCombo->setCurrentIndex(m_cdSpeedIndex);
        }

        enableWrite();
        disableCheck();
        break;
    }

    case DvdModel::dsEmptyDvd : {
        blankDataLabel->setText("BLANK");
        statusLabel->setText("DVD-R(W)");

        if (deviceType >= DvdModel::dtDvdWriter) {
            if (m_speedList != 2) {
                speedCombo->addItem("1");
                speedCombo->addItem("2");
                speedCombo->addItem("4");
                speedCombo->addItem("6");
                speedCombo->addItem("8");
                speedCombo->addItem("10");
                speedCombo->addItem("12");
                speedCombo->addItem("16");

                if (m_dvdSpeedIndex < 0)
                    m_dvdSpeedIndex = 4;

                m_speedList = 2;
                speedCombo->setCurrentIndex(m_dvdSpeedIndex);
            }

            enableWrite();
            disableCheck();
        } else {
            wideStatusLabel->setText("Unsupported media by this device!");
            disableWrite();
            disableCheck();
        }
        break;
    }

    case DvdModel::dsEmptyBd : {
        blankDataLabel->setText("BLANK");
        statusLabel->setText("BD-R(E)");

        if (deviceType >= DvdModel::dtBdWriter) {
            if (m_speedList != 2) {
                speedCombo->addItem("1");
                speedCombo->addItem("2");
                speedCombo->addItem("4");
                speedCombo->addItem("8");
                speedCombo->addItem("12");
                speedCombo->addItem("15");

                if (m_bdSpeedIndex < 0)
                    m_bdSpeedIndex = 4;

                m_speedList = 2;
                speedCombo->setCurrentIndex(m_bdSpeedIndex);
            }

            enableWrite();
            disableCheck();
        } else {
            wideStatusLabel->setText("Unsupported media by this device!");
            disableWrite();
            disableCheck();
        }
        break;
    }

    case DvdModel::dsDataCd : {
        speedCombo->clear();
        m_speedList = 0;


        statusLabel->setText("CD-R(W)");
        blankDataLabel->setText("DATA");

        disableWrite();
        enableCheck();
        break;
    }


    case DvdModel::dsDataDvd : {
        speedCombo->clear();
        m_speedList = 0;

        statusLabel->setText("DVD-R(W)");
        blankDataLabel->setText("DATA");

        disableWrite();
        enableCheck();
        break;
    }

    case DvdModel:: dsDataBd : {
        speedCombo->clear();
        m_speedList = 0;

        statusLabel->setText("BD-R(E)");
        blankDataLabel->setText("DATA");

        disableWrite();
        enableCheck();
        break;
    }
    };


    if ((processMode != DvdModel::pmWriting) &&
        (processMode != DvdModel::pmChecking)) {
        enableCombos();
        stopAnimation();
    } else {
        disableCombos();
    }

    switch (processMode) {
        case DvdModel::pmNone: {
            if (dvdStatus == DvdModel::dsEmptyCd || dvdStatus == DvdModel::dsEmptyDvd || dvdStatus == DvdModel::dsEmptyDvd) {
                wideStatusLabel->setText(QString("ISO Size %1 M / Disk capacity %2 M")
                                         .arg(m_isoSize  / 1024 / 1024)
                                         .arg(capacity  / 1024 / 1024));
                hashLabel2->setText("");
            }

            if ((m_isoSize == 0) || (m_isoSize > capacity)) {
                disableWrite();
            }

            break;
        }

        case DvdModel::pmWriting: {
            hashLabel2->setText("Writing...");
            startBurnAnimation();
            disableWrite();
            disableCheck();
            enableCancel();
            break;
        }

        case DvdModel::pmChecking: {
            hashLabel2->setText("Calculating...");
            startCheckAnimation();
            disableWrite();
            disableCheck();
            enableCancel();
            break;
        }


        case DvdModel::pmWaiting: {
            break;
        }

        case DvdModel::pmWriteDone: {
            taskStatusLabel->setEnabled(true);
            taskStatusLabel->setText("Burned");

            if ((dataCheck->checkState() == Qt::PartiallyChecked) ||
                (dataCheck->checkState() == Qt::Checked)) {
                m_model->checkSum(m_isoSize);
            }

            disableCancel();
            break;
        }

        case DvdModel::pmCheckDone: {
            if ((hash.size() > 0) && (m_isoMd5sum.size() > 0)) {
                if (hash.compare(m_isoMd5sum)==0) {
                    taskStatusLabel->setEnabled(true);
                    taskStatusLabel->setText("md5 good");
                }
                else {
                    taskStatusLabel->setEnabled(false);
                    taskStatusLabel->setText("md5 bad");
                }
            }
            disableCancel();
            break;
        }

        case DvdModel::pmCheckFailed: {
            taskStatusLabel->setEnabled(false);
            taskStatusLabel->setText("md5 fail");
            hashLabel2->setText("I/O Error: can't read md5sum");
            disableCancel();
            break;
        }

        case DvdModel::pmWriteFailed: {
            taskStatusLabel->setEnabled(false);
            taskStatusLabel->setText("Burn fail");
            hashLabel2->setText("Canceled by user or I/O Error");
            disableCheck();
            disableCancel();
            break;
        }
    };
}

void DvdView::enableWrite() {
    burnButton->setEnabled(true);
    speedCombo->setEnabled(true);
}

void DvdView::disableWrite() {
    burnButton->setEnabled(false);
    speedCombo->setEnabled(false);
}

void DvdView::enableCheck() {
    checkButton->setEnabled(true);
}

void DvdView::disableCheck() {
    checkButton->setEnabled(false);
}

void DvdView::enableCancel() {
    cancelButton->setEnabled(true);
}

void DvdView::disableCancel() {
    cancelButton->setEnabled(false);
}

void DvdView::enableCombos() {
    categoryCombo->setEnabled(true);
    isoCombo->setEnabled(true);
    menuButton->setEnabled(true);
}

void DvdView::disableCombos() {
    categoryCombo->setEnabled(false);
    isoCombo->setEnabled(false);
    menuButton->setEnabled(false);
}

void DvdView::disableGui() {
    setEnabled(false);
}

void DvdView::startBurnAnimation() {
    if (!m_timer) {
        m_timer = new QTimer();

        m_timer->start(1000);

        connect(m_timer, SIGNAL(timeout()),
                this,  SLOT(burnAnimate()));
    }
}

void DvdView::stopAnimation() {
    if (m_timer) {
        m_timer->stop();
        delete m_timer;
        m_timer = 0;
    }
}

void DvdView::burnAnimate() {
    m_animateFlag = !m_animateFlag;
    if (m_animateFlag) {
        iconLabel->setPixmap(icon(QLatin1String("burn_on")));
    } else {
        iconLabel->setPixmap(icon(QLatin1String("burn_off")));
    }
    iconLabel->update();
}

void DvdView::startCheckAnimation() {
    if (!m_timer)
        m_timer = new QTimer();

    m_timer->start(1000);

    connect(m_timer, SIGNAL(timeout()), this,  SLOT(checkAnimate()));
}

void DvdView::checkAnimate() {
    m_animateFlag = !m_animateFlag;
    if (m_animateFlag) {
        iconLabel->setPixmap(icon(QLatin1String("check_on")));
    } else {
        iconLabel->setPixmap(icon(QLatin1String("check_off")));
    }
}

void DvdView::progress(int value, const QString &info) {
    progressBar->setValue(value);
    wideStatusLabel->setText(info);
}

// Ui Slots

void DvdView::on_dataCheck_stateChanged(int state) {

}

void DvdView::on_categoryCombo_currentIndexChanged(QString category) {
    m_category = category;

    QStringList rootDirs = Application::paths();

    qDebug() << rootDirs;

    isoCombo->clear();
    foreach (QString path, rootDirs) {
        QDir vDir(QString("%1/%2").arg(path).arg(m_category));

        QStringList nameFilters;
        nameFilters << QLatin1String("*.[iI][sS][oO]");   

        QStringList fileNames = vDir.entryList(nameFilters, QDir::Files);
        foreach (QString fileName, fileNames) {
            if (isoCombo->findText(fileName) == -1)
                isoCombo->addItem(fileName);
        }
    }
}

void DvdView::on_isoCombo_currentIndexChanged(QString isoFileName) {
    QStringList rootDirs = Application::paths();
    m_isoMd5sum = QLatin1String("");
    foreach (QString path, rootDirs) {
        m_isoFile = QDir::toNativeSeparators(QString("%1/%2/%3")
                                            .arg(path).arg(m_category).arg(isoFileName));
        QFileInfo fileInfo(m_isoFile);
        if (fileInfo.exists()) {
            m_isoSize = fileInfo.size();

            QString fileName = QDir::toNativeSeparators(QString("%1/%2/%3.md5sum")
                                                         .arg(path).arg(m_category).arg(isoFileName));
            fileInfo.setFile(fileName);
            if (fileInfo.exists()) {
                QFile file(fileName);
                if (file.open(QIODevice::ReadOnly)) {
                    QString data = file.readAll();
                    if (data == QLatin1String("calculating...")) {
                        m_isoMd5sum = tr("calculating...");
                    } else {
                        QStringList splittedData = data.split(QLatin1String(" "),
                                                                QString::SkipEmptyParts);
                        if (splittedData.size() == 2) {
                            m_isoMd5sum = splittedData.at(0);
                        }
                    }
                    file.close();
                }
            }
            break;
        } else {
            m_isoSize = 0;
        }
    }
    hashLabel1->setText(m_isoMd5sum);
    if (m_model) {
        m_model->update();
    }
}


void DvdView::on_speedCombo_currentIndexChanged(int index) {
    if (index >= 0) {
        if (m_speedList == 1) {
            m_cdSpeedIndex = index;
        } else
        if (m_speedList == 2) {
            m_dvdSpeedIndex = index;
        }
    }
}

void DvdView::on_checkButton_clicked() {
    if (m_model)
        m_model->checkSum(m_isoSize);

}

void DvdView::on_burnButton_clicked() {
    if (m_model) {
        m_model->setSpeed(speedCombo->currentText().toInt());
        m_model->setBuffer(bufferEdit->text());
        m_model->setStreamRecording(streamRecordingCheck->isChecked());
        m_model->burnISO(m_isoFile);
    }
}

void DvdView::on_cancelButton_clicked() {
    if (m_model)
        m_model->cancel();

}

void DvdView::on_menuButton_clicked() {
    if (m_menu) {
        delete m_menu;
        m_menu = 0;
    }

    m_menu = new QMenu();
    QAction *action = m_menu->addAction(tr("Refresh categories from disk (for this view)"));
    connect(action, SIGNAL(triggered()), this, SLOT(refreshRootDirsSlot()));
    action = m_menu->addAction(tr("Refresh categories from disk (for all views)"));
    connect(action, SIGNAL(triggered()), Application::instance(), SLOT(refreshPaths()));
    m_menu->addSeparator();
    action = m_menu->addAction(tr("Generate md5sum files for attached ISO collection"));
    connect(action, SIGNAL(triggered()), Application::instance(), SLOT(checkMd5SumFiles()));

    m_menu->popup(menuButton->mapToGlobal(QPoint(0,0)));
}

void DvdView::refreshRootDirsSlot() {
    QStringList rootDirs = Application::paths();

    categoryCombo->clear();
    foreach (QString path, rootDirs) {
        QDir dir(path);
        QStringList categories = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
        foreach (QString category, categories) {
            if (categoryCombo->findText(category) == -1)
                categoryCombo->addItem(category);
        }
    }
}
