/***************************************************************************
 *   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 <iostream>
#include "amarokData.h"
#include <vdr/tools.h>

// static elements
namespace
{
enum PLAYLIST_TAG
{
    PLAYLIST_TITLE,
    PLAYLIST_ALBUM,
    PLAYLIST_ARTIST,
    PLAYLIST_GENRE,
    PLAYLIST_LENGTH,
    PLAYLIST_BITRATE,
    PLAYLIST_YEAR,
    PLAYLIST_COMMENT,
    PLAYLIST_SIZE
};

const char* PLAYLIST_TAGS [PLAYLIST_SIZE]=
    {
        "<Title>",
        "<Album>",
        "<Artist>",
        "<Genre>",
        "<Length>",
        "<Bitrate>",
        "<Year>",
        "<Comment>"
    };

const char *PLAYLIST_START_TAG ="<playlist ";
const char *PLAYLIST_END_TAG ="</playlist>";
const char *LIST_START_TAG ="<list ";
const char *LIST_END_TAG ="</list>";
const char *ITEM_START_TAG ="<item>";
const char *ITEM_END_TAG ="</item>";
const char *SUBITEM_START_TAG ="<sub>";
const char *CONTENT_EMPTY_STRING="-";

};


/////////////////////////////////////////////////////////////////////////
////////// PlaylistItem
/////////////////////////////////////////////////////////////////////////

PlaylistItem::PlaylistItem(const char* title,
                           const char* album,
                           const char* artist,
                           const char *genre,
                           const char* length,
                           const char* bitrate,
                           const char* year,
                           const char* comment)
        :Title(title),Album(album),Artist(artist),Genre(genre),Length(length),Bitrate(bitrate),Year(year),Comment(comment)
{}


/** get play list item in short text format */
void PlaylistItem::getShortTextContent(char *str,int index)
{

    //memset(str, 0x00, MAX_ITEM_CHAR_SIZE);
    if (index>=0)
        snprintf(str, MAX_ITEM_CHAR_SIZE, "%d. %s - %s", index+1, Title, Artist); // we start from 1 !!!
    else
        snprintf(str, MAX_ITEM_CHAR_SIZE, "%s - %s",Title, Artist);

}


/////////////////////////////////////////////////////////////////////////
////////// TrackItem
/////////////////////////////////////////////////////////////////////////

TrackItem::TrackItem(PlaylistItem* item, int index, int sumTr, const std::string& stats)
        :PlaylistItem(item->Title,item->Album,item->Artist,item->Genre,item->Length,item->Bitrate, item->Year, item->Comment)
{
    Index=index;
    TrackSum=sumTr;

    Statistics = stats;
}

void TrackItem::getFormatTimeTrIdx(char* str,const char* preTime,const char* preTrack,int cTime,bool less)
{
    int sum=atoi(Length);
    if (!less)
        snprintf(str,MAX_ITEM_CHAR_SIZE,
                 "%s: %02d:%02d/ %02d:%02d           %s:%02d/ %02d",
                 preTime,cTime/60,cTime%60,sum/60,sum%60,preTrack,Index+1,TrackSum);
    else
        snprintf(str,MAX_ITEM_CHAR_SIZE,
                 "%s: -%02d:%02d/ %02d:%02d           %s: %02d/ %02d",
                 preTime,(sum-cTime)/60,(sum-cTime)%60,sum/60,sum%60,preTrack,Index+1,TrackSum);
}



/////////////////////////////////////////////////////////////////////////
////////// AmarokData
/////////////////////////////////////////////////////////////////////////

AmarokData::AmarokData(AMAROK_CMD type) : m_type (type)
{}

AmarokData::~AmarokData()
{
    clearList();
}

using namespace std;

#ifdef AMAROK_DEBUG
void AmarokData::debugList()
{
    std::vector<std::string>::iterator p;

    p=m_strCorelist.begin();
    while (p != m_strCorelist.end())
    {
        cout << *p++ << endl;
    }
}
#endif
int AmarokData::getInteger()
{
    if (m_strCorelist.size())
    {
        return atoi(m_strCorelist[0].c_str());
    }
    else
        return -1;
}

const string AmarokData::getString () const
{
    if (m_strCorelist.size())
    {
        return m_strCorelist[0];
    }

    return 0;
}

void AmarokData::clearList()
{
    m_strCorelist.clear();
}

bool AmarokData::getBoolean()
{
    if (m_strCorelist.size() && m_strCorelist[0].find("true")==0)
        return true;
    else
        return false;
}

const char* AmarokData::getConstChar()
{
    if (!m_strCorelist.size())
        return m_strCorelist[0].c_str();
    else
        return NULL;
}


/////////////////////////////////////////////////////////////////////////
////////// AmarokDataExt
/////////////////////////////////////////////////////////////////////////
AmarokDataExt::AmarokDataExt(AMAROK_CMD type):AmarokData (type)
{}


void AmarokDataExt::getTagContent(const string &line, string &tag, const char** content)
{
    unsigned int index=line.find_first_of(">")+1;
    if( index != string::npos ) // no tag in line
    {

        tag=line.substr(0,index);
        if (index<line.size())
            *content= &(line[index]); //mmh , don't see c_str() solution !?
        else
            *content=CONTENT_EMPTY_STRING;
    }
    else // no tag found !!
        *content=0;


}


/////////////////////////////////////////////////////////////////////////
////////// AmarokPlaylist
/////////////////////////////////////////////////////////////////////////

AmarokPlaylist::AmarokPlaylist():AmarokDataExt(CMD_REQUEST_PLAYLIST)
{}

AmarokPlaylist::~AmarokPlaylist()
{
    clearList();
}

void AmarokPlaylist::clearList()
{
    AmarokData::clearList();

    if (!m_playList.empty())
    {
        for (unsigned int index =0; index< m_playList.size();index++)
        {
            PlaylistItem* p=m_playList[index];
            delete p;
        }

        m_playList.clear();
    }

}
#ifdef AMAROK_DEBUG
void AmarokPlaylist::debugList()
{

    for (unsigned int index =0; index< m_playList.size();index++)
    {

        PlaylistItem* p=m_playList[index];
        for (int i=0; i<PLAYLIST_SIZE; i++)
        {

            cout <<	PLAYLIST_TAGS[i] << "=";
            switch (i)
            {
            case PLAYLIST_TITLE:
                cout << p->Title;
                break;
            case PLAYLIST_ALBUM:
                cout << p->Album;
                break;
            case PLAYLIST_ARTIST:
                cout << p->Artist;
                break;
            case PLAYLIST_GENRE:
                cout << p->Genre;
                break;
            case PLAYLIST_LENGTH:
                cout << p->Length;
                break;
            case PLAYLIST_BITRATE:
                cout << p->Bitrate;
                break;
            default:
                AmarokTracer.errorOut("debug ERROR");
                ;
            }
            cout << endl;

        }// for PLAYLIST_TAGS
    }

}
#endif
void AmarokPlaylist::getPlaylistItem (PlaylistItem **item, unsigned int index)
{
    if (getType() != CMD_REQUEST_PLAYLIST || m_playList.empty() )
        *item=0;
    else
        *item=m_playList[index];

}

void AmarokPlaylist::removePlaylistItem (unsigned int index)
{
    if (m_playList.empty() )
        return;
    else
    {
        std::vector<PlaylistItem*>::iterator p=m_playList.begin();
        p+=index;
        /*cout << "Erasing item:";
        char str[MAX_ITEM_CHAR_SIZE];
        (*p)->getShortTextContent(str);
        cout  << str << endl;
        */
        delete (*p);
        m_playList.erase(p);

    }

}

void AmarokPlaylist::buildList()
{

    if (getType() == CMD_REQUEST_PLAYLIST)
    {
        const char *title=0,*album=0,*artist=0,*genre=0,*length=0,*br=0, *year=0, *comment=0;
        title=album=artist=genre=length=br=year=comment=CONTENT_EMPTY_STRING;
        bool newItem=false;

        for(unsigned int i=0 ; i<m_strCorelist.size(); i++)
        {
            string tag;
            const char *content=CONTENT_EMPTY_STRING;
            if (m_strCorelist[i].size()>1)
                getTagContent(m_strCorelist[i],tag,&content);
            else
                continue;

            if (!content)
            {
                esyslog( "Tag missing: %s", m_strCorelist[i].c_str());
                break;
            }
            if (tag.find(PLAYLIST_START_TAG)==0)
                continue;
            else if (tag.find(PLAYLIST_END_TAG)==0)
                break;
            else if (tag.find(ITEM_START_TAG)==0)
            {
                if (newItem)
                {
                    esyslog("XML ERROR - exspecting %s",ITEM_END_TAG);
                    break;
                }
                newItem = true;

                title=album=artist=genre=length=br=year=comment=CONTENT_EMPTY_STRING;
            }
            else if (tag.find(ITEM_END_TAG)==0)
            {
                if (!newItem)
                {
                    esyslog("XML ERROR - exspecting %s",ITEM_START_TAG);
                    break;
                }
                newItem = false;
                m_playList.push_back(new PlaylistItem(title,album,artist,genre,length,br,year, comment));
            }
            else
            {
                if (!newItem)
                {
                    esyslog("XML ERROR - missing %s not %s",ITEM_START_TAG, tag.c_str());
                    break;
                }
                bool stop=false;
                for (int i=0; i<PLAYLIST_SIZE && !stop; i++)
                {

                    if (tag.find(PLAYLIST_TAGS[i])==0)
                    {
                        switch (i)
                        {
                        case PLAYLIST_TITLE:
                            title=content;
                            break;
                        case PLAYLIST_ALBUM:
                            album=content;
                            break;
                        case PLAYLIST_ARTIST:
                            artist=content;
                            break;
                        case PLAYLIST_GENRE:
                            genre=content;
                            break;
                        case PLAYLIST_LENGTH:
                            length=content;
                            break;
                        case PLAYLIST_BITRATE:
                            br=content;
                            break;
                        case PLAYLIST_YEAR:
                            year=content;
                            break;
                        case PLAYLIST_COMMENT:
                            comment=content;
                            break;
                        default:
                            esyslog("PARSER ERROR: %s",PLAYLIST_TAGS[i]);

                        }
                        stop=true;

                    }
                } // for

                // if (!stop)
                //     cout << tag << " tag has been ignored" << endl;

            } //else

        }// while

    }


}



/////////////////////////////////////////////////////////////////////////
////////// AmarokQueryResultItem
/////////////////////////////////////////////////////////////////////////


AmarokQueryResultItem::AmarokQueryResultItem(const char* id) : p_id(id) , b_SubShow(false)
{}


const char* AmarokQueryResultItem::getSubItem (unsigned int index )
{
    if (index<=m_SubItemlist.size())
        return m_SubItemlist[index];
    else
        return 0;
}




/////////////////////////////////////////////////////////////////////////
////////// AmarokQueryResultlist
/////////////////////////////////////////////////////////////////////////

AmarokQueryResult::AmarokQueryResult():AmarokDataExt(CMD_REQUEST_LIST)
{}

AmarokQueryResult::~AmarokQueryResult()
{
    clearList();
}


void AmarokQueryResult::clearList()
{
    if ( m_ResultList.size())
    {
        for (unsigned int index =0; index< m_ResultList.size();index++)
        {
            AmarokQueryResultItem* p=m_ResultList[index];
            delete p;
        }

        m_ResultList.clear();
        m_strCorelist.clear();
    }
}

#ifdef AMAROK_DEBUG

void AmarokQueryResult::debugList()
{
    cout << "m_ResultList.size()" << m_ResultList.size() << endl;
    for (unsigned int index =0; index< m_ResultList.size();index++)
    {
        AmarokQueryResultItem* p=m_ResultList[index];
        cout << index+1<< ". " << p->getId() << endl;
        for (unsigned int sub =0; sub< p->getSubItemSize();sub++)
        {
            cout << "\t" << p-> getSubItem(sub) << endl;
        }
    }


}
#endif

AmarokQueryResultItem* AmarokQueryResult::getItem(unsigned int index) const
{
    if (index<=m_ResultList.size())
    {
        return m_ResultList[index];
    }
    else
        return 0;

}

AmarokQueryResultItem* AmarokQueryResult::getItemByOsdIndex(unsigned int index)
{
    unsigned int node =getNodeIndex(index);
    if (node<=m_ResultList.size())
    {
        return m_ResultList[node];
    }
    else
        return 0;

}


bool AmarokQueryResult::getItemFormated(char *str,unsigned int index, unsigned int &subsize)
{
    bool bRet=false;
    if (index<=m_ResultList.size())
    {
        AmarokQueryResultItem* p=m_ResultList[index];
        if (p->getSubItemSize()>=1)
            snprintf(str, MAX_ITEM_CHAR_SIZE, "+%s", p->getId());
        else
            snprintf(str, MAX_ITEM_CHAR_SIZE, "-%s", p->getId());

        subsize=p->getSubItemSize();
        bRet=p->getShow();
    }

    return bRet;
}

unsigned int AmarokQueryResult::getNodeIndex(unsigned int osdIndex)
{
    unsigned int osd=0;
    if (osdIndex==0) // no difference for first !
        return 0;

    for (unsigned int node =0; node< m_ResultList.size();node++,osd++)
    {
        if (osdIndex==osd)
            return node;

        if (AmarokQueryResultItem* item=getItem(node))
        {
            if (item->getShow())
            {
                for (unsigned int sub=0; sub< item->getSubItemSize(); sub++,osd++)
                {
                    if (osdIndex==osd)
                    {
                        esyslog("getNodeIndex:Not a node");
                        return node;
                    }
                }
            }
        }
    }
    return 0;
}

const char* AmarokQueryResult::getTextItemByOsdIndex(unsigned int search) const
{
    unsigned int counter= 0;

    for (unsigned int index =0; index< m_ResultList.size();index++,counter++)
    {

        if (AmarokQueryResultItem* item=getItem(index))
        {
            if (search == counter) // when node
                return item->getId();
            if (item->getShow())
            {
                for (unsigned int sub=0; sub< item->getSubItemSize(); sub++)
                {
                    counter++;
                    if (search == counter) // when subitem
                        return item->getSubItem(sub);
                }
            }
        }
    }
    return 0;
}

bool AmarokQueryResult::getSubItemFormated(char *str,unsigned int index,unsigned int subindex )
{

    bool bRet=false;
    if (index<=m_ResultList.size())
    {
        AmarokQueryResultItem* p=m_ResultList[index];

        if (p->getSubItemSize()>=subindex)
        {
            snprintf(str, MAX_ITEM_CHAR_SIZE, " |- %s", p->getSubItem( subindex));
            bRet=true;
        }
    }
    return bRet;
}

void AmarokQueryResult::getListItem (AmarokQueryResultItem **item, unsigned int index)
{
    if (m_ResultList.empty() )
        *item=0;
    else
        *item=m_ResultList[index];

}


void AmarokQueryResult::buildList()
{
    AmarokQueryResultItem* item=0;

    for(unsigned int i=0 ; i<m_strCorelist.size(); i++)
    {
        string tag;
        const char *content=0;
        if (m_strCorelist[i].size()>1)
            getTagContent(m_strCorelist[i],tag,&content);
        else
            continue;

        if (content) // tag found
        {
            if (tag.find(LIST_START_TAG)==0)
                continue;
            else if (tag.find(LIST_END_TAG)==0)
                break;
            else if (tag.find(ITEM_START_TAG)==0)
            {
                item = new  AmarokQueryResultItem(content);
                m_ResultList.push_back(item);
            }
            else if (tag.find(SUBITEM_START_TAG)==0 && item)
            {
                item->addSubItem(content);
            }

        }
        //else ignore

    }
}

