/***************************************************************************
 *   Copyright (C) 2005 by Roland Weigert   *
 *   roweigert@t-online.de   *
 *                                                                         *
 *   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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "tagguesser.h"

tagguesser::tagguesser(fileselector *treewidget)
{
    changedtags=FALSE;
    directorytree=treewidget;
    workingfilters.setAutoDelete(TRUE);
    initfilters();
    //This one is set to default-filter, when more filters are matching
    defaultfilter=NULL;
}

tagguesser::~tagguesser()
{
    savefilters();
}

bool tagguesser::checkfile(fileselectoritem *itemtocheck)
{
    filename=itemtocheck->itemfilename;
    //throw out anything concerning a path
    while (filename.contains("/"))
        filename=filename.remove(0,1);
    success=FALSE;
    //go through all filters for file, and let user choose the right filter if more seem to match or enter new filter if nothing matches
    matchingfilters.clear();
    QPtrList<guessfilter>::Iterator filterindex=workingfilters.begin();
    while (filterindex!=workingfilters.end())
    {
        //Use a backup of the original filename for matching
        QString backupfilename=filename;
        //match actual filename against actual filter
        guessfilter *actualfilter=*filterindex;
        cerr<<actualfilter->filtertext<<endl;
        QStringList::Iterator mnemnomicindex=actualfilter->mnemnomiclist.begin();
        bool nextfilter=FALSE;
        while (mnemnomicindex!=actualfilter->mnemnomiclist.end()&&!nextfilter)
        {
            int command=0;
            if ((command=checkMemnomic(QString(*mnemnomicindex))))
            {
                switch (command)
                {
                case 1:
                case 4:
                    {
                        //Get any numbers from this position on
                        QString creatednumber;
                        QChar buffer;
                        bool nomorenumber=FALSE;
                        while (!nomorenumber)
                        {
                            buffer=backupfilename.at(0);
                            if (buffer.isDigit())
                            {
                                creatednumber.append(buffer);
                                //Do not remove the signs with remove(buffer), otherwise we remove signs we need later!
                                backupfilename.remove(0,1);
                            }
                            else
                                nomorenumber=TRUE;
                        }
                        if (!creatednumber.length())
                        {
                            nextfilter=TRUE;
                        }
                        break;
                    }
                case 7:
                    {
                        //The text to the end should be mp3 or ogg
                        if ((backupfilename.lower().compare("mp3")&&backupfilename.lower().compare("ogg")))
                        {
                            nextfilter=TRUE;
                        }
                        break;
                    }
                }
                mnemnomicindex++;
            }
            else
            {
                /*Its no known mnemomic, so we have to find the signs in the current string
                 *If we don't match, we can ignore the rest of the filter, and try the next one*/
                int position=0;
                if (!(QString(*mnemnomicindex).compare(".")))
                {
                    //find the first point, and remove all following
                    if((position=backupfilename.find("."))!=-1)
                    {
                        //cut beginning of string, untill the searched signs
                        backupfilename=backupfilename.remove(0,position+1);
                        while(backupfilename.startsWith("."))
                        {
                            backupfilename=backupfilename.remove(0,1);
                            cerr<<backupfilename<<endl;
                        }
                        mnemnomicindex++;
                    }
                    else
                        nextfilter=TRUE;
                }
                else
                {
                    if((position=backupfilename.find(QString(*mnemnomicindex)))!=-1)
                    {
                        //cut beginning of string, untill the searched signs
                        backupfilename=backupfilename.remove(0,position+(QString(*mnemnomicindex).length()));
                        mnemnomicindex++;
                    }
                    else
                    {
                        nextfilter=TRUE;
                    }
                }
            }
        }
        if (!nextfilter)
        {
            //put actual filter in the matchingfilterslist
            matchingfilters.append(*filterindex);
        }
        filterindex++;
    }
    switch (matchingfilters.count())
    {
    case 0:
        {
            if(generateNewFilter(filename))
            {
                guess(itemtocheck);
                success=TRUE;
            }
            else
                success=FALSE;
            break;
        }
    default:
        {
            //Check if default value is already set, and if it is in the list
            if(!defaultfilter||matchingfilters.find(defaultfilter)==-1)
            {
                QString question=QString(i18n("<center><h3>Select the correct filter for:</h3>%1</center>")).arg(filename);
                choicebox *userchoice=new choicebox(&matchingfilters,question);
                while(userchoice->selectedfilter==QString::null&&!userchoice->abort)
                {
                    kapp->processEvents();
                }
                if (!userchoice->abort)
                {
                    //We have a filterstring from user, check if it says new filter, or find it in our list
                    if (userchoice->selectedfilter.contains("new filter"))
                    {
                        //generate a new filter
                        delete userchoice;
                        if(generateNewFilter(filename))
                        {
                            guess(itemtocheck);
                            success=TRUE;
                        }
                        else
                            success=FALSE;
                    }
                    else
                    {
                        //Find the right filter in the list, and set it to the new defaultfilter
                        setFilter(userchoice->selectedfilter);
                        delete userchoice;
                        guess(itemtocheck);
                        success=TRUE;
                    }
                }
                else
                {
                    delete userchoice;
                    success=FALSE;
                }
            }
            else
            {
                //use the defaultfilter,cause it seems to match
                guess(itemtocheck);
                success=TRUE;
            }
            break;
        }
    }
    return success;
}

void tagguesser::initfilters()
{
    //load all filters from disk, if the filters-file already exists
    workingfilters.clear();
    QString buffer;
    QFile *filterfile=new QFile(QString("%1/.k-yamo.filters").arg(QDir::homeDirPath()));
    //If filterfile exists, store the read out filters in Stringlist
    if(filterfile->open(IO_ReadOnly))
    {
        QTextStream readfilters(filterfile);
        readfilters.setEncoding(QTextStream::UnicodeUTF8);
        while(!readfilters.atEnd())
        {
            buffer=readfilters.readLine();
            //Check if line is correct terminated with an ; but remove this sign at end
            if (buffer.endsWith(";"))
            {
                buffer=buffer.left(buffer.findRev(";"));
                filterlist.append(buffer);
            }
        }
        filterfile->close();
        delete filterfile;
    }
    if (filterlist.isEmpty())
    {
        //Init the stringlist with default values, and store this list fterwards
        filterlist.append("#BAND# #ALBUM# #TRACK#-#SONG#.#FORMAT#;");
        filterlist.append("#BAND#-#ALBUM#-_-#TRACK#.#SONG#.#FORMAT#;");
        filterlist.append("#ALBUM#-#BAND#-_-#TRACK#-#SONG#.#FORMAT#;");
        savefilters();
    }
    /*Now init all read-in filters into memory
      We have to make a backup of the filterlist, cause we need to delete unwanted filters from the original-list
      This would lead into a crash, when we are working on the same list, as we process*/
    QStringList backuplist;
    backuplist=filterlist;
    QStringList::Iterator filterindex=backuplist.begin();
    while (filterindex!=backuplist.end())
    {
        guessfilter *check=new guessfilter(*filterindex);
        if (!check->filterok)
        {
            //Throw away that filter, because it does not work
            filterlist.remove(*filterindex);
        }
        else
        {
            //Store all initialized filters in QPtrList workingfilters for later use
            workingfilters.append(check);
        }
        filterindex++;
    }
    //Now that we checked all of the existing filters, save all ok-ones back to disk
    savefilters();
}

void tagguesser::savefilters()
{
    QFile *filterfile=new QFile(QString("%1/.k-yamo.filters").arg(QDir::homeDirPath()));
    if(filterfile->open(IO_WriteOnly))
    {
        QStringList::Iterator index=filterlist.begin();
        QTextStream writefilters(filterfile);
        writefilters.setEncoding(QTextStream::UnicodeUTF8);
        while (index!=filterlist.end())
        {
            //Write filter with a trailing ;-sign
            writefilters<<QString(*index)<<";"<<"\n";
            index++;
        }
        filterfile->close();
        delete filterfile;
    }
}

bool tagguesser::setFilter(QString filtertoset)
{
    QPtrList<guessfilter>::Iterator searchindex=matchingfilters.begin();
    bool found=FALSE;
    while(searchindex!=matchingfilters.end()&&!found)
    {
        if((*searchindex)->filtertext.compare(filtertoset))
            searchindex++;
        else
        {
            found=TRUE;
            defaultfilter=*searchindex;
        }
    }
    return found;
}

bool tagguesser::generateNewFilter(QString pattern)
{
    bool userabort=FALSE;
    //generate the new filter interactive with the user out of the given filename
    filtergenerator *testfilter=new filtergenerator(pattern);
    while (!testfilter->ready)
        kapp->processEvents();
    if (testfilter->ready==-1)
        userabort=TRUE;
    else
    {
        guessfilter *check=new guessfilter(testfilter->filter);
        //Check for double entries, and don't enter them in this list.
        if(!setFilter(testfilter->filter))
        {
            workingfilters.append(check);
            filterlist.append(testfilter->filter);
            defaultfilter=check;
            savefilters();
        }
    }
    delete testfilter;
    return !userabort;
}

void tagguesser::guess(fileselectoritem *selecteditem)
{
    QString inputname=selecteditem->itemfilename;
    //throw out anything concerning a path
    while (inputname.contains("/"))
        inputname=inputname.remove(0,1);
    //Go through all mnemnomics, and set the values in mediatag Song
    //Use a backup of the original filename for matching
    //match actual filename against the set default
    mediatag Song;
    Song.songlength=selecteditem->taginfo.songlength;
    QStringList::Iterator mnemnomicindex=defaultfilter->mnemnomiclist.begin();
    while (mnemnomicindex!=defaultfilter->mnemnomiclist.end())
    {
        int command=0;
        if ((command=checkMemnomic(QString(*mnemnomicindex))))
        {
            //get next memnomic, to know how far to read
            QString nextmnemnomic;
            if(mnemnomicindex!=defaultfilter->mnemnomiclist.end())
                nextmnemnomic=QString(*(++mnemnomicindex));
            switch (command)
            {
            case 2:
                {
                    //artist
                    if(nextmnemnomic.isEmpty())
                        Song.artist=inputname;
                    else if (!nextmnemnomic.compare("."))
                    {
                        //Read to the first point, and than check if others follow
                        int position=inputname.find(".");
                        while(inputname.at(position+1)=='.')
                            position++;
                        Song.artist=inputname.left(position);
                        inputname=inputname.remove(0,position);

                    }
                    else if (nextmnemnomic.compare("."))
                    {
                        //Read to the next memnomic
                        int position=inputname.find(nextmnemnomic);
                        Song.artist=inputname.left(position);
                        inputname=inputname.remove(0,position);
                    }
                    //throw out unwanted underscores,and replace them with spaces
                    Song.artist=Song.artist.replace('_',' ');
                    break;
                }
            case 3:
                {
                    //song
                    if(nextmnemnomic.isEmpty())
                        Song.title=inputname;
                    else if (!nextmnemnomic.compare("."))
                    {
                        //Read to the first point, and than check if others follow
                        int position=inputname.find(".");
                        while(inputname.at(position+1)=='.')
                            position++;
                        Song.title=inputname.left(position);
                        inputname=inputname.remove(0,position);

                    }
                    else if (nextmnemnomic.compare("."))
                    {
                        //Read to the next memnomic
                        int position=inputname.find(nextmnemnomic);
                        Song.title=inputname.left(position);
                        inputname=inputname.remove(0,position);
                    }
                    //throw out unwanted underscores,and replace them with spaces
                    Song.title=Song.title.replace('_',' ');
                    break;
                }
            case 1:
            case 4:
                {
                    //Get any numbers from this position on
                    QString creatednumber;
                    QChar buffer;
                    bool nomorenumber=FALSE;
                    while (!nomorenumber)
                    {
                        buffer=inputname.at(0);
                        if (buffer.isDigit())
                        {
                            creatednumber.append(buffer);
                            inputname.remove(0,1);
                        }
                        else
                            nomorenumber=TRUE;
                    }
                    bool ok;
                    if (command==1)
                        Song.year=creatednumber.toInt(&ok,10);
                    else if (command==4)
                        Song.number=creatednumber.toInt(&ok,10);
                    break;
                }
            case 5:
                {
                    //album
                    if(nextmnemnomic.isEmpty())
                        Song.album=inputname;
                    else if (!nextmnemnomic.compare("."))
                    {
                        //Read to the first point, and than check if others follow
                        int position=inputname.find(".");
                        while(inputname.at(position+1)=='.')
                            position++;
                        Song.album=inputname.left(position);
                        inputname=inputname.remove(0,position);

                    }
                    else if (nextmnemnomic.compare("."))
                    {
                        //Read to the next memnomic
                        int position=inputname.find(nextmnemnomic);
                        Song.album=inputname.left(position);
                        inputname=inputname.remove(0,position);
                    }
                    //throw out unwanted underscores,and replace them with spaces
                    Song.album=Song.album.replace('_',' ');
                    break;
                }
            case 6:
                {
                    //genre
                    if(nextmnemnomic.isEmpty())
                        Song.genre=inputname;
                    else if (!nextmnemnomic.compare("."))
                    {
                        //Read to the first point, and than check if others follow
                        int position=inputname.find(".");
                        while(inputname.at(position+1)=='.')
                            position++;
                        Song.genre=inputname.left(position);
                        inputname=inputname.remove(0,position);

                    }
                    else if (nextmnemnomic.compare("."))
                    {
                        //Read to the next memnomic
                        int position=inputname.find(nextmnemnomic);
                        Song.genre=inputname.left(position);
                        inputname=inputname.remove(0,position);
                    }
                    //throw out unwanted underscores,and replace them with spaces
                    Song.genre=Song.genre.replace('_',' ');
                    break;
                }
            }
        }
        else
        {
            //Its no known mnemomic, so we have to find the signs in the current string
            int position=0;
            if (!QString(*mnemnomicindex).compare("."))
            {
                //Check if we have more than one point, and put it in the right place
                if((position=inputname.find("."))!=-1)
                {
                    //cut beginning of string, untill the searched sign
                    inputname=inputname.remove(0,position+(QString(*mnemnomicindex).length()));
                    while (inputname.startsWith("."))
                        inputname=inputname.remove(0,1);
                    mnemnomicindex++;
                }
            }
            else
            {
                if((position=inputname.find(QString(*mnemnomicindex)))!=-1)
                {
                    //cut beginning of string, untill the searched sign
                    inputname=inputname.remove(0,position+(QString(*mnemnomicindex).length()));
                    mnemnomicindex++;
                }
            }
        }
    }
    /*First try to match all values with our local database
    * if that does not work,or user doesn't use database , pull results from musicbrainz*/
    if (Song.album.isEmpty()||Song.artist.isEmpty()||Song.title.isEmpty()||!Song.number||!Song.year)
    {
        //Looks like we need a bit help out of database or from musicbrainz, cause tags are not complete
        bool match=FALSE;
        if (settingsfile.databasetype)
        {
            //code follows

        }
        if (!match&&settingsfile.musicbrainzsupport)
        {
            mbhelp->findsong(&Song);
        }
    }
    //export results into our given item
    //Only change the tags, if the tagpart has something in it
    if (Song.number)
        selecteditem->taginfo.number=Song.number;
    if(!Song.title.isEmpty())
        selecteditem->taginfo.title=Song.title;
    if(!Song.album.isEmpty())
        selecteditem->taginfo.album=Song.album;
    if(!Song.artist.isEmpty())
        selecteditem->taginfo.artist=Song.artist;
    if(!Song.genre.isEmpty())
        selecteditem->taginfo.genre=Song.genre;
    if(Song.year)
        selecteditem->taginfo.year=Song.year;
    //set actual item to modified-state, so user sees that something has changed
    directorytree->setedited(selecteditem);
    changedtags=TRUE;
}

guessfilter::guessfilter(QString text)
{
    filtertext=text;
    //set filterok=FALSE, and return to calling routine if /-sign is in filter
    if (filtertext.contains("/"))
    {
        errortext=i18n("No /-Signs are allowed in filter!");
        filterok=FALSE;
        return;
    }
    QString actualfilter=filtertext;
    //compare up to the first memnomic found
    int mnemnomicposition;
    while((mnemnomicposition=actualfilter.find("#"))!=-1)
    {
        if (mnemnomicposition)
        {
            QString comparestring=actualfilter.left(mnemnomicposition);
            mnemnomiclist.append(comparestring);
            actualfilter.remove(0,mnemnomicposition);
        }
        //Check if the text between the both #-signs is a valid memnomic
        mnemnomicposition=actualfilter.find("#",1);
        QString mnemnomicstring=actualfilter.left(mnemnomicposition+1);
        actualfilter=actualfilter.remove(0,mnemnomicposition+1);
        if(checkMemnomic(mnemnomicstring))
            mnemnomiclist.append(mnemnomicstring);
        else
        {
            //This is no valid filter, throw it away, cause #-is only allowed for mnemnomics
            errortext=i18n("No #-Signs are allowed in filter. They are reserved for the mnemnomics!");
            filterok=FALSE;
            return;
        }
    }
    if (!actualfilter.isEmpty())
    {
        //The rest of the filterstring, not containing any #-signs.
        mnemnomiclist.append(actualfilter);
    }
    //if we get here, the filter should be ok, and not contain unwanted signs
    filterok=TRUE;
}

guessfilter::~guessfilter()
{}
