/***************************************************************************
 *   Copyright (C) 2006 by Jens Koehler                                    *
 *   irimi_at_gmx_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 <cstdio>
#include <string>
#include <iostream>
#include <fstream>
#include <cctype>

#include "commands.h"
#include "amarokgateway.h"
#include "debugTracer.h"



namespace // static components
{

const int NUM_ENTITY = 5;

const char* ITEM_URL_TAG ="<item ";
const char* ITEM_TAG ="<item>";
const char* ITEM_END_TAG= "</item>";
const char* PLAYLIST_END_TAG="</playlist>";
//const char* LIST_END_TAG="</list>";
const char* SUB_TAG ="<sub>";
const char* END_TAG ="</";

const char* DCOP_BASIC_CMD ="dcop amarok ";

// can be used for future releases !
char *TAG_IGNORE_LIST []=
    {
        "<Directory>",
        "<Score>",
        "<Type>",
        "<Playcount>",
        "<LastPlayed>",
        "<Filesize>",
        "<Composer>",
        "<DiscNumber>",
        "<Mood>",
        "<SampleRate>",
        "<?xml version"
    };


};

AmarokGateway::Entity AmarokGateway::entity[ NUM_ENTITY ] = 
{
    { "&amp;",  5, '&' },
    { "&lt;",   4, '<' },
    { "&gt;",   4, '>' },
    { "&quot;", 6, '\"' },
    { "&apos;", 6, '\'' }
};


using namespace std;

AmarokGateway::AmarokGateway(const char *user,
                             const char *pw,
                             const char* album,
                             const char* artist,
                             const char* genre ) : CoreGateway (user,pw),
        p_album_cache_file(album),
        p_artist_cache_file(artist),
        p_genre_cache_file(genre)
{}

AmarokGateway::~AmarokGateway()
{}

bool AmarokGateway::checkVersion()
{
    vector<string> tmp;

    if (popen("dcop amarok player version", tmp) && tmp.size()==1 && tmp[0].size()>=2)
    {
        const char *p=tmp[0].c_str();

        if (p[2]>='4')
            return true;

        cerr << "wrong amarok version:" << tmp[0] << endl;
    }

    tracer.errorOut("ERROR: Couldn't attach to amaroK 1.4.x");
    return false;
}


bool AmarokGateway::requestData(const string &cmd_parameter, vector<string> &result)
{
    string cmd = DCOP_BASIC_CMD;
    cmd.append(cmd_parameter);
    return popen(cmd,result);
}


bool AmarokGateway::popen ( const string &command, vector<string> &result)
{
    FILE*           fp;
    const int       BUFFERSIZE = 500;
    char            buf [BUFFERSIZE];
    string      strLine;

    bool bRet=true;
    result = vector<string> ();
    //cout << command.c_str() << endl;
    if ((fp = ::popen(command.c_str (), "r")) == NULL)
    {
        tracer.errorOut("popen cannot be created");
        return false;
    }

    while (fgets(buf, sizeof (buf), fp))
    {
        strLine = buf;

        //    if (strLine [strLine.size () - 1] != '\n')
        //    {
        //      tracer.errorOut("BUFFERSIZE too small:" + BUFFERSIZE );
        //      bRet = false;
        //    }
        //    else
        //    {
        if (strLine.find("no such function")==0)
        {
            tracer.errorOut("Dcop:no such function");
            bRet = false;
        }
        else
        {   // remove \n
            result.push_back (strLine.substr (0, strLine.length()- 1));
        }
        //    }
    }
    if (pclose(fp) == -1)
    {
        tracer.errorOut("Cannot execute pclose");
        bRet = false;
    }
    return bRet;
}

bool AmarokGateway::readPlaylistFile (vector<string> &result)
{

    string      strLine;
    string      strFile  = result[0];

    bool bRet=true;
    result = vector<string> ();

    ifstream in(strFile.c_str());
    if (!in)
    {
        tracer.errorOut("Cannot open xml playlist");
        bRet = false;
    }
    else
    {
        while(in && getline(in,strLine) && !in.eof())
        {
            if (!checkIgnoreReduceData(strLine))
                result.push_back (strLine);
        }
    }
    return bRet;

}

void AmarokGateway::addMedias(const vector<string> &list)
{
    for(unsigned int ii=0 ; ii<list.size(); ii++)
    {
        addMedia(list[ii]);
    }
}

bool AmarokGateway::addMedia(const string &url)
{
    vector<string> result;
    string cmd=DCOP_BASIC_CMD;
    cmd.append("playlist addMedia \"");
    if ('.'==url[0])
        cmd.append(url.substr(1,url.length())); // apears to be an amarok problem
    else
        cmd.append(url);
    cmd.append("\"");
    return popen(cmd, result);

}

bool AmarokGateway::queryAmarok(const string &query,vector<string> &result)
{

    return popen("dcop amarok collection query \"" +query+"\"", result);

}


void AmarokGateway::addArtists(const vector<string> &artistList)
{

    for(unsigned int i=0 ; i<artistList.size(); i++)
    {

        if (artistList[i][0]!=' ')
        {
            vector<string>urlList;
            string query = "SELECT DISTINCT t.url FROM album al, artist ar, tags t WHERE t.artist = ar.id AND t.album = al.id AND ar.name = '";
            query.append(artistList[i]);
            query.append("' order by al.name , t.track");
            queryAmarok(query,urlList);
            addMedias(urlList);
        }
        else
            continue;
    }

}


void AmarokGateway::addAlbums(const vector<string> &albumList)
{

    for(unsigned int i=0 ; i<albumList.size(); i++)
    {

        if (albumList[i][0]!=' ')
        {
            vector<string>urlList;
            string query = "SELECT DISTINCT t.url FROM album al, artist ar, tags t WHERE t.artist = ar.id AND t.album = al.id  AND  al.name = '";
            query.append(albumList[i]);
            query.append("' order by t.track");
            queryAmarok(query,urlList);
            addMedias(urlList);
        }
        else
            continue;
    }

}

void AmarokGateway::addTitles(const std::vector<std::string> &titleList)
{
    for(unsigned int i=0 ; i<titleList.size(); i++)
    {

        if (titleList[i][0]!=' ')
        {
            vector<string>urlList;
            // @todo new query definition for titles
            string query = "SELECT DISTINCT t.url FROM album al, artist ar, tags t WHERE t.artist = ar.id AND t.album = al.id  AND  al.name = '";
            query.append(titleList[i]);
            query.append("' order by t.track");
            queryAmarok(query,urlList);
            addMedias(urlList);
        }
        else
            continue;
    }

}

void AmarokGateway::addGenres (const std::vector<std::string> &genreList)
{
    for(unsigned int i=0 ; i<genreList.size(); i++)
    {

        if (genreList[i][0]!=' ')
        {
            vector<string>urlList;
            // @todo new query definition for genres

            string query ="SELECT DISTINCT t.url FROM genre gr, artist ar, tags t WHERE t.artist = ar.id AND t.genre = gr.id  AND  gr.name = '";
            query.append(genreList[i]);
            query.append("' order by t.artist");
            queryAmarok(query,urlList);
            addMedias(urlList);
        }
        else
            continue;
    }

}

/*
bool AmarokGateway::queryCurrentTrackStatistics(vector<string> &result)
{
 
  vector<string> tmp;
 
  if (popen("dcop amarok player encodedURL", tmp) && tmp.size()==1)
  {
    string query = "SELECT playcounter, percentage, rating FROM statistics WHERE url = '";
    query.append(tmp[0]);
    query.append("'");
    return popen("dcop amarok collection query \""+query+"\"", result);
  }
 
  return false;
 
}
 
*/
bool AmarokGateway::queryArtist(const string &qArtist, vector<string> &result)
{
    string query = "SELECT DISTINCT artist.name FROM tags INNER JOIN artist ON artist.id=tags.artist WHERE tags.sampler = 0 ";
    if (qArtist.size())
    {
        query.append("and artist.name like '%");
        query.append(qArtist);
        query.append("%'");
    }
    query.append(" ORDER BY LOWER( artist.name ) ");
    return popen("dcop amarok collection query \""+query+"\"", result);
}

bool AmarokGateway::queryAlbumArtist(const string &qAlbum, vector<string> &result)
{
    string query = "SELECT DISTINCT al.name from album al, artist ar, tags t where t.artist = ar.id and t.album = al.id and ar.name = '";
    query.append(qAlbum);
    query.append("' ORDER BY LOWER( al.name ) ");

    return popen("dcop amarok collection query \""+query+"\"", result);
}

bool AmarokGateway::queryGenreArtist(const string &qGenre, vector<string> &result)
{
    string query = "SELECT DISTINCT ar.name from genre gr, artist ar, tags t where t.artist = ar.id and t.genre=gr.id and gr.name='";
    query.append(qGenre);
    query.append("' ORDER BY LOWER( ar.name ) ");

    return popen("dcop amarok collection query \""+query+"\"", result);
}

bool AmarokGateway::queryGenre(const string &qGenre, vector<string> &result)
{
    string query = "SELECT DISTINCT genre.name FROM tags INNER JOIN genre ON genre.id=tags.genre WHERE tags.sampler = 0 ";
    if (qGenre.size())
    {
        query.append("and genre.name like '%");
        query.append(qGenre);
        query.append("%'");
    }
    query.append(" ORDER BY LOWER( genre.name ) ");

    return popen("dcop amarok collection query \""+query+"\"", result);
}



bool AmarokGateway::queryAlbum(const string &qAlbum, vector<string> &result)
{
    string query ="SELECT DISTINCT album.name FROM album ";
    if (qAlbum.size())
    {
        query.append("and album.name like '%");
        query.append(qAlbum);
        query.append("%'");
    }
    query.append(" ORDER BY lower( album.name ) ");
    return popen("dcop amarok collection query \""+query+"\"", result);
}



/**filter data we are not interested in to reduce needed bandwith
and remove xml end tag */
bool AmarokGateway::checkIgnoreReduceData(string &str)
{
    // remove leading spaces
    unsigned int i=0;
    unsigned int index =str.find_first_not_of (" ");
    str=str.substr(index,str.length()-index);
    // do nothing / ignore
    if (0 == str.find(ITEM_END_TAG) || 0 == str.find(PLAYLIST_END_TAG) )
        return false;

    // current str in ignore list to drop?
    for(i = 0; i < sizeof(TAG_IGNORE_LIST) / sizeof(char*); i++)
    {
        if ( 0 == str.find(TAG_IGNORE_LIST[i]) )
            return true;
    }

    // redefine item tag
    if (str.find(ITEM_URL_TAG)==0)
        str.assign(ITEM_TAG);
/*    else 
    {
        bool bFound=false;
      //redefine xml special characters (e.g. "&amp;" -> "&")
        for(i = 0; i < NUM_ENTITY ; i++)
        {
         do 
         {  
           bFound=false;
           index = str.find_first_of(entity[i].str);
           if ( index != 0 && index < str.length() )
           {
             cout << "replace " << str << " to ";
             str = str.replace(index,entity[i].strLength,1,entity[i].chr);
             bFound = true; // look for more than one
             cout << str << endl;
           }
         }while(bFound);
        }
       
    }*/
    
    // remove xml end tag
    str= str.substr (0, str.find(END_TAG));
    return false;
}

// generate list which is simple to parse
bool AmarokGateway::buildArtistsResultlist (vector<string>&result)
{
    return loadCache ( p_artist_cache_file,result );
}

// generate list which is simple to parse
bool AmarokGateway::buildAlbumsResultlist (vector<string>&result)
{
    return loadCache ( p_album_cache_file,result );
}

bool AmarokGateway::buildPlayResultlist (const string &cmd_parameter,vector<string>&result)
{

    if (requestData(cmd_parameter,result))
    {
        if  (result.size()>=1)
        {
            return readPlaylistFile(result);
        }
    }

    return false;

}

bool AmarokGateway::listRequest (TCPStream &stream,int int_cmd, const string &cmd_parameter, vector<string>&result)
{
    if ( int_cmd == CMD_REQUEST_PLAYLIST )
    {
        return buildPlayResultlist(cmd_parameter,result);
    }
    else
    {
        return CoreGateway::listRequest (stream, int_cmd, cmd_parameter, result);
    }


}


// generate list which is simple to parse
bool AmarokGateway::buildGenresResultlist (vector<string>&result)
{
    return loadCache ( p_genre_cache_file,result );
}

bool AmarokGateway::checkCache ()
{
    bool btest=false;
    vector<string>result;

    btest=loadCache ( p_album_cache_file,result );
    if (btest)
        btest=loadCache ( p_artist_cache_file,result );
    if (btest)
        btest=loadCache ( p_genre_cache_file,result );

    // @todo check amarok database entry size for changes to update cache

    return btest;
}
// generate list which is simple to parse
bool AmarokGateway::buildArtistsResultlistCache ()
{
    bool bRet=true;
    vector<string>artistList;
    vector<string>albumList;
    vector<string>result;

    if(bRet=queryArtist("",artistList)) // get all
    {

        for(unsigned int i=0 ; i<artistList.size(); i++)
        {
            string text=ITEM_TAG;
            text.append(artistList[i]);
            result.push_back(text);
            if(bRet=queryAlbumArtist(artistList[i],albumList))
            {

                for(unsigned int ii=0 ; ii<albumList.size(); ii++)
                {
                    if (albumList[ii].length()>1)
                        result.push_back(SUB_TAG+albumList[ii]);
                }

            }
            else
                break;
        }
    }

    if (bRet)
        return saveCache ( p_artist_cache_file,result );

    return bRet;
}

// generate list which is simple to parse
bool AmarokGateway::buildAlbumsResultlistCache ()
{
    bool bRet=true;
    vector<string>result;
    vector<string>albumList;

    if(bRet=queryAlbum("",albumList)) // get all
    {

        for(unsigned int i=0 ; i<albumList.size(); i++)
        {
            string text=ITEM_TAG;
            text.append(albumList[i]);
            //cout << text << endl;
            result.push_back(text);
        }
    }
    if (bRet)
        return saveCache ( p_album_cache_file,result );

    return bRet;
}

// generate list which is simple to parse
bool AmarokGateway::buildGenresResultlistCache ()
{
    bool bRet=true;
    vector<string>result;
    vector<string>genreList;
    vector<string>artistList;

    if(bRet=queryGenre("",genreList)) // get all
    {

        for(unsigned int i=0 ; i<genreList.size(); i++)
        {
            string text=ITEM_TAG;
            text.append(genreList[i]);
            result.push_back(text);
            if(bRet=queryGenreArtist(genreList[i],artistList))
            {

                for(unsigned int ii=0 ; ii<artistList.size(); ii++)
                {
                    if (artistList[ii].length()>1)
                        result.push_back(SUB_TAG+artistList[ii]);
                }

            }
            else
                break;
        }
    }

    if (bRet)
        return saveCache ( p_genre_cache_file,result );

    return bRet;
}

