/*
 *   This file is part of Auralquiz
 *   Copyright 2011-2017  JanKusanagi JRR <jancoding@gmx.com>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .
 */


#include "musicanalyzer.h"


MusicAnalyzer::MusicAnalyzer(QString musicDir,
                             QString dataDir,
                             QStringList *musicFiles,
                             QWidget *parent)  :  QWidget(parent)
{
    this->musicDirectory = musicDir;
    this->dataDirectory = dataDir;

    this->musicFiles = musicFiles;


    qDebug() << "MusicAnalyzer created";
}


MusicAnalyzer::~MusicAnalyzer()
{

    qDebug() << "MusicAnalyzer destroyed";
}


void MusicAnalyzer::setMusicDirectory(QString musicDir)
{
    this->musicDirectory = musicDir;

    qDebug() << "MusicAnalyzer: music directory set to " << musicDirectory;
}

void MusicAnalyzer::storeMetadataCache()
{
    // Store info about the scanned music files
    QFile cachedMetadata0(this->dataDirectory + "/musicFiles0.aq");
    cachedMetadata0.open(QIODevice::WriteOnly);
    QString cachedMetadata0Item;

    QFile cachedMetadata1(this->dataDirectory + "/musicFiles1.aq");
    cachedMetadata1.open(QIODevice::WriteOnly);
    QString cachedMetadata1Item;

    QFile cachedMetadata2(this->dataDirectory + "/musicFiles2.aq");
    cachedMetadata2.open(QIODevice::WriteOnly);
    QString cachedMetadata2Item;

    QFile cachedMetadata3(this->dataDirectory + "/musicFiles3.aq");
    cachedMetadata3.open(QIODevice::WriteOnly);
    QString cachedMetadata3Item;

    qDebug() << "Datafiles created";

    for (int counter = 0; counter < this->musicFiles[0].length(); ++counter)
    {
        // filenames
        cachedMetadata0Item = musicFiles[0].at(counter);
        cachedMetadata0Item.append("\n");
        cachedMetadata0.write(cachedMetadata0Item.toUtf8());

        // FIXME: some encoding problems with tags

        // artists
        cachedMetadata1Item = musicFiles[1].at(counter);
        cachedMetadata1Item.append("\n");
        cachedMetadata1.write(cachedMetadata1Item.toUtf8());

        // titles
        cachedMetadata2Item = musicFiles[2].at(counter);
        cachedMetadata2Item.append("\n");
        cachedMetadata2.write(cachedMetadata2Item.toUtf8());

        // albums
        cachedMetadata3Item = musicFiles[3].at(counter);
        cachedMetadata3Item.append("\n");
        cachedMetadata3.write(cachedMetadata3Item.toUtf8());
    }

    cachedMetadata0.close();
    cachedMetadata1.close();
    cachedMetadata2.close();
    cachedMetadata3.close();

    qDebug() << "Metadata stored in .aq files";
}



///////////////////////////////////////////////////////////////////////////////
////////////////////////////////// SLOTS //////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////



/*
 *  Load music collection info from files created in previous scan
 *
 */
void MusicAnalyzer::loadSongList()
{
    qDebug() << "MusicAnalyzer::loadSongList()";
    if (this->musicDirectory.isEmpty())
    {
        emit setStartGameButton(false, tr("Configure your music folder"), "");
        qDebug() << "Music directory not configured, aborting scan";

        return;
    }

    QFile cachedMetaData0(this->dataDirectory + "/musicFiles0.aq");
    cachedMetaData0.open(QIODevice::ReadOnly);

    QFile cachedMetaData1(this->dataDirectory + "/musicFiles1.aq");
    cachedMetaData1.open(QIODevice::ReadOnly);

    QFile cachedMetaData2(this->dataDirectory + "/musicFiles2.aq");
    cachedMetaData2.open(QIODevice::ReadOnly);

    QFile cachedMetaData3(this->dataDirectory + "/musicFiles3.aq");
    cachedMetaData3.open(QIODevice::ReadOnly);


    if (!cachedMetaData0.isReadable() || !cachedMetaData1.isReadable()
     || !cachedMetaData2.isReadable() || !cachedMetaData3.isReadable())
    {
        cachedMetaData0.close();
        cachedMetaData1.close();
        cachedMetaData2.close();
        cachedMetaData3.close();
        qDebug() << "Couldn't load some of the cached metadata files; "
                    "creating song list";

        this->createSongList();
        return;
    }

    this->musicFiles[0].clear();
    this->musicFiles[1].clear();
    this->musicFiles[2].clear();
    this->musicFiles[3].clear();

    /*
     * FIXME: PLENTY OF ERROR CONTROL MISSING HERE
     */

    QString fileName;
    QString artistName;
    QString songTitle;
    QString albumTitle;

    qDebug() << "Loading cached metadata from files";
    while (!cachedMetaData0.atEnd())
    {
        // FIXME: separate this mess (avoids encoding problems)
        fileName = QString::fromUtf8(cachedMetaData0.readLine(1024).trimmed().data());
        musicFiles[0].append(fileName);

        // FIXME: assuming Utf8 in artist and title tags, for now
        artistName = QString::fromUtf8(cachedMetaData1.readLine(1024).trimmed().data());
        musicFiles[1].append(artistName);

        songTitle = QString::fromUtf8(cachedMetaData2.readLine(1024).trimmed().data());
        musicFiles[2].append(songTitle);

        albumTitle = QString::fromUtf8(cachedMetaData3.readLine(1024).trimmed().data());
        musicFiles[3].append(albumTitle);
    }

    cachedMetaData0.close();
    cachedMetaData1.close();
    cachedMetaData2.close();
    cachedMetaData3.close();

    emit setStartGameButton(true, "", "");


    qDebug() << "loadSongList() done";
}





/*
 *  Analyze all songs in given directory, get the metadata and store it
 *
 */
void MusicAnalyzer::createSongList()
{
    qDebug() << "MusicAnalyzer::createSongList()";


    emit setStartGameButton(false, tr("Analyzing music. Please wait"), "");

    this->musicFiles[0].clear(); // make file-list empty
    this->musicFiles[1].clear(); // make artists-list empty
    this->musicFiles[2].clear(); // make titles-list empty
    this->musicFiles[3].clear(); // make albums-list empty



    QProgressDialog fileListerProgressDialog(tr("Getting list of files under\n"
                                                "%1",
                                                "%1 is a folder")
                                           .arg(musicDirectory),
                                           tr("Cancel"),
                                           0, 10000,
                                             this);
    fileListerProgressDialog.setWindowTitle(tr("Listing files...")
                                            + " - Auralquiz");
    fileListerProgressDialog.setMinimumWidth(400);
    fileListerProgressDialog.setMinimumDuration(0);
    fileListerProgressDialog.show();

    QStringList directoryFiles;

    qDebug() << "Listing files under" << musicDirectory;
    QDirIterator dirList(QDir(this->musicDirectory,
                              "*.mp3 *.ogg *.oga *.flac *.opus",
                              QDir::NoSort, QDir::Files),
                         QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);

    while (dirList.hasNext())
    {
        directoryFiles.append(dirList.next());

        // update progressbar
        fileListerProgressDialog.setValue(directoryFiles.length());
        qApp->processEvents();
        if (fileListerProgressDialog.wasCanceled())
        {
            qDebug() << "createSongList(): file listing cancelled by button";
            emit setStartGameButton(false,
                                    tr("Please reload music info"),
                                    tr("Music analysis was cancelled"));

            fileListerProgressDialog.reset();
            return; // you sure?
        }

    }
    fileListerProgressDialog.close();


    if (directoryFiles.length() == 0)
    {
        qDebug() << "There are no files!";
        QString errorExplanation = tr("There are no valid music files "
                                      "in your selected music directory.")
                                   + "\n"
                                   + tr("Please, select another directory "
                                        "containing Ogg, FLAC or MP3 files, "
                                        "and try again.");
        QMessageBox::critical(this,
                              tr("There is no music!"),
                              errorExplanation);

        emit setStartGameButton(false,
                                tr("Please choose another music folder"),
                                errorExplanation);

        return;
    }


    // Start analyzer loop
    int validFiles = 0;
    int totalFiles = directoryFiles.length();

    QString totalFilesString = QLocale::system().toString(totalFiles);
    QProgressDialog analyzerProgressDialog(tr("Analyzing %1 files\n"
                                              "under %2",
                                              "%1 is a number, %2 is a folder")
                                           .arg(totalFilesString)
                                           .arg(musicDirectory),
                                           tr("Cancel analysis"),
                                           0, totalFiles,
                                           this);
    analyzerProgressDialog.setWindowTitle(tr("Analyzing your music...")
                                          + " - Auralquiz");
    analyzerProgressDialog.setMinimumWidth(400);
    analyzerProgressDialog.setMinimumDuration(0); // Always show


    for (int currentFile = 0; currentFile != totalFiles; ++currentFile)
    {
        qDebug() << "currentFile:" << currentFile+1 << "of" << totalFiles;
        qDebug() << "filename:" << directoryFiles.at(currentFile);

        QString artistTag;
        QString titleTag;
        QString albumTag;

        TagLib::FileRef tagFile(directoryFiles.at(currentFile).toUtf8());
        if (!tagFile.isNull())
        {
            artistTag = QString::fromStdWString(tagFile.tag()->artist().toWString());
            titleTag = QString::fromStdWString(tagFile.tag()->title().toWString());
            albumTag = QString::fromStdWString(tagFile.tag()->album().toWString());

            qDebug() << artistTag << titleTag << albumTag;
            qDebug() << tagFile.audioProperties()->length();
        }
        else
        {
            qDebug() << "-- tagFile error:" << directoryFiles.at(currentFile).toUtf8();
            qDebug() << "-- tagFile name - " << tagFile.file()->name();
            sleep(1);
        }


        if (!artistTag.isEmpty() && !titleTag.isEmpty())
        {
            qDebug() << "Has artist AND title, OK ->" << artistTag << titleTag;
            qDebug() << "Album, if any            ->" << albumTag;
            this->musicFiles[0].append(directoryFiles.at(currentFile));

            this->musicFiles[1].append(artistTag);
            this->musicFiles[2].append(titleTag);
            this->musicFiles[3].append(albumTag);

            ++validFiles;
        }
        else
        {
            qDebug() << "This file doesn't seem to have metadata, "
                        "or a related error occurred";
        }

        analyzerProgressDialog.setValue(currentFile); // update progressbar
        qApp->processEvents();
        if (analyzerProgressDialog.wasCanceled())
        {
            qDebug() << "createSongList(): analysis cancelled by button";
            musicFiles[0].clear();
            musicFiles[1].clear();
            musicFiles[2].clear();
            musicFiles[3].clear();

            emit setStartGameButton(false, tr("Please reload music info"), "");

            return; // you sure?
        }

    } // end analyzer loop



    // We're done scanning

    qDebug() << "\n\n---------------- SCAN COMPLETE -------";
    //memory required for the lists?
    qDebug() << "memory used, about: "<< musicFiles[0].length()*40*4 << "bytes";
    qDebug() << "Storing data in:" << this->dataDirectory;
    qDebug() << "Valid files:" << validFiles;


    if (validFiles < 6)  // If not enough files
    {
        qDebug() << "ERROR: Not enough valid files (i.e. no metadata in them)";

        // FIXME: tmp message in disabled button
        emit setStartGameButton(false,
                                tr("Please choose another music folder"),
                                tr("The folder you selected doesn't have "
                                   "enough valid music files"));

        return;
    }


    this->storeMetadataCache();

    // songlist is ready, allow starting game
    emit setStartGameButton(true, tr("&Start game"), "");


    qDebug() << "start button re-enabled and with focus";
}

