#include <fstream>
#include <vdr/interface.h>
#include <vdr/menu.h>
#include <vdr/plugin.h>
#include "search.h"
#include "i18n.h"
#include "player-mp3.h"
#include "data-mp3.h"
#include "decoder.h"
#include "mp3id3.h"
#include "mp3control.h"
#include "skin.h"
#include "vars.h"

#define X_DISPLAY_MISSING

// ------------ cMP3SearchBrowseItem ------------------------------------------------------------------------------------------------ //

class cMP3SearchBrowseItem : public cOsdItem {
private:
 cFileObj *item;
 virtual void SetID3(void);
 int idx;
 int total;
 int bitrate;
 const char *artist;
 const char *title;
public:
 cMP3SearchBrowseItem(cFileObj *Item, int index, const char *Artist, const char *Title, const char *Bitrate, const char *Total);
 cFileObj *Item(void) {return item;}
 void NewName(void);
};

cMP3SearchBrowseItem::cMP3SearchBrowseItem(cFileObj *Item, int index, const char *Artist, const char *Title, const char *Bitrate, const char *Total) {
  item      = Item;
  idx       = index +1;
  total     = atoi(Total);
  bitrate   = atoi(Bitrate);
  artist    = Artist;
  title     = Title;

  SetID3();
}


void cMP3SearchBrowseItem::SetID3(void) {
  char *Name;

#ifdef HAVE_TTF_CHARS
  if(MP3Setup.ArtistFirst) 
    asprintf(&Name, "%s \t %s - %s \t  %3d k    \t%02d:%02d", "\u00FA", artist, title, bitrate/1000, total/60, total%60);
  else 
    asprintf(&Name, "%s \t %s - %s \t  %3d k    \t%02d:%02d", "\u00FA", title, artist, bitrate/1000, total/60, total%60);
#else
  if(MP3Setup.ArtistFirst) 
    asprintf(&Name, "%04d \t %s - %s \t  %3d k    \t%02d:%02d", idx, artist, title, bitrate/1000, total/60, total%60);
  else 
    asprintf(&Name, "%04d \t %s - %s \t  %3d k    \t%02d:%02d", idx, title, artist, bitrate/1000, total/60, total%60);
#endif

  SetText(Name,false);
}




void cMP3SearchBrowseItem::NewName(void) {
  char *Name;
  cSong *song= new cSong(item);
  cSongInfo *si = song->Info(false);

  if(!si) si=song->Info();

#ifdef HAVE_TTF_CHARS
  if(si && si->Title) {
    if(MP3Setup.ArtistFirst) 
      asprintf(&Name, "%s \t %s - %s \t  %3d k    \t%02d:%02d", "\u00F9", si->Artist, si->Title,si->Bitrate/1000, si->Total/60, si->Total%60);
    else 
      asprintf(&Name, "%s \t %s - %s \t  %3d k    \t%02d:%02d", "\u00F9", si->Title, si->Artist, si->Bitrate/1000, si->Total/60, si->Total%60);
    }
  else {
      asprintf(&Name, "%s \t <%s> \t               \t         ", "\u00F9", song->Name());
  } 
#else
  if(si && si->Title) {
    if(MP3Setup.ArtistFirst) 
      asprintf(&Name, "%s \t %s - %s \t  %3d k    \t%02d:%02d", "<<  ", si->Artist, si->Title,si->Bitrate/1000, si->Total/60, si->Total%60);
    else 
      asprintf(&Name, "%s \t %s - %s \t  %3d k    \t%02d:%02d", "<<  ", si->Title, si->Artist, si->Bitrate/1000, si->Total/60, si->Total%60);
    }
  else {
      asprintf(&Name, "%s \t <%s> \t               \t         ", "<<  ", song->Name());
  } 
#endif
 
  SetText(Name,false);

}



// ------------ cMP3SearchResult ------------------------------------------------------------------------------------------------------------

cFileObj *cMP3SearchResult::selected=0;


cMP3SearchResult::cMP3SearchResult(const char *szTitle)
:cOsdMenu(szTitle ,GetTab(1), GetTab(2), GetTab(3), GetTab(4)) //, GetTab(5))
{

  if(LoadResult()) {
    SetHelp();
    }
  else
    Skins.Message(mtError, tr("Error while opening @Suchergebnis.m3u !"));

    Display();
}


cMP3SearchResult::~cMP3SearchResult()
{
}


cFileObj *cMP3SearchResult::CurrentItem(void) {
  cMP3SearchBrowseItem *item = (cMP3SearchBrowseItem *)Get(Current());
  return item ? item->Item():0;
}  


void cMP3SearchResult::SetHelp() {
  cOsdMenu::SetHelp(tr("Parent"),tr("Add"),tr("Add all"),tr("ID3info"));
}


int cMP3SearchResult::GetTab(int Tab)
{
  if (Tab==1)    return  5;
  if (Tab==2)    return  26; // org 30
  if (Tab==3)    return  8;
  if (Tab==4)    return  7;
  return 0;
}  


int cMP3SearchResult::LoadResult() {

 using namespace std;
  std::string SongTitle("");
  std::string Base("");
  std::string basesource("");
  std::string newbase("");
  std::string datei("");
  std::string FullPath("");
  std::string Artist("");
  std::string Title("");
  std::string Bitrate("");
  std::string Total("");
  bool result = false;
  std::string filename("");
  int  Length;
 
  basesource = BaseSource;
  Length = basesource.length();
  
  filename = basesource;
  filename = filename + "/@Suchergebnis.m3u";
  
  ofstream out(filename.c_str());

  datei  = basesource + "/@Suche.dat";

  if(FILE* fd = fopen(datei.c_str(), "r")) {
    char line[1024];
    const char* delim = ";";
    char* word = NULL;
    int i=0;
    bool base = false;
  
    while(fgets(line, sizeof(line), fd) != NULL) {
      word = strtok(line,delim);
      for(i=0; word !=NULL; i++) {

        if(i==0) FullPath=word;
          else 
            if(i==1) Artist=word;
              else      
                if(i==2) Title=word;
                  else
                    if(i==3) Bitrate=word;
                      else
                        if(i==4) Total=word;
      
        word = strtok(NULL,delim);
      }


      newbase = FullPath.substr(0, Length);

      // check for same basedir , so substr cant crash
      if(basesource == newbase) {

        string::size_type pos = FullPath.rfind ('/',FullPath.length());

        if(pos!=string::npos) {

	   // Songname
          SongTitle =  FullPath.substr(pos+1,FullPath.length());
          // Basedir
          Base      =  FullPath.substr(Length +1, FullPath.length() - Length - SongTitle.length() -1);
          if(Base.length() > 0) { 
	    Base = Base.substr(0, Base.length() -1);
            base = true;
          }
          else {
            base = false;
          }
        }     

        newsong=new cFileObj(MP3Sources.GetSource(), base ? Base.c_str() : 0, SongTitle.c_str(),otFile);

        if( newsong && (newsong->Type()==otFile) ) {
          Add(new cMP3SearchBrowseItem(newsong, count, Artist.c_str(), Title.c_str(), Bitrate.c_str(), Total.c_str()));
          d(printf("music: search: found '%s'\n", FullPath.c_str()));
          out<<FullPath.c_str()<<endl;
          }
        else {
          delete newsong;       
        }
      }
    }

    out.close();
    fclose(fd);

    result = true;
  }

  return result;

}

eOSState cMP3SearchResult::ID3Info(void) {
  cFileObj *item=CurrentItem();
  if(item && item->Type()==otFile) {
    cSong *song=new cSong(item);
    cSongInfo *si;
    if(song && (si=song->Info())) {
      AddSubMenu(new cMP3SearchID3Info(si, item->Path()));
      }
  }

  return osContinue;
}        


eOSState cMP3SearchResult::AddOne(void) {
  cFileObj *item=CurrentItem();
  
  if((item=CurrentItem())) {
    selected=new cFileObj(item);
    cInstantPlayList *newpl= new cInstantPlayList(item);
    if(newpl->Load() && newpl->Count()) {
      isyslog("music: added track %s to playlist %s", item->Name(),newpl->Name());
      cMP3Control::SetPlayList(newpl);

      cMP3SearchBrowseItem *cur = (cMP3SearchBrowseItem *)Get(Current());
      cur->NewName();
      Display();

      return osContinue;
      }
    else
      delete newpl;
    }

  return osContinue;    
}      



eOSState cMP3SearchResult::AddAll(void) {
  Skins.Message(mtStatus, tr("Add all tracks to playlist..."));

  cFileObj *item=new cFileObj(MP3Sources.GetSource(), 0, "@Suchergebnis.m3u",otFile);
  cPlayList *newpl=new cPlayList(item);

  if(newpl->Load() && newpl->Count()) {
    isyslog("music: added all tracks to playlist %s", newpl->Name());
    cMP3Control::SetPlayList(newpl);
    Skins.Message(mtStatus, NULL);
    return osBack;
    }

  delete newpl;
  Skins.Message(mtStatus, NULL);

  return osContinue;
}      


eOSState cMP3SearchResult::ProcessKey(eKeys Key) {

  bool hadSubmenu = HasSubMenu();

  eOSState state = cOsdMenu::ProcessKey(Key);

  if (hadSubmenu && !HasSubMenu())
    return osContinue;


  if (state == osUnknown) {
    switch (Key) {

      case kUp:
      case kUp|k_Repeat:
      case kDown:
      case kDown|k_Repeat:
	  break;

      case kRed:
	  return osBack;
	break;

      case kGreen:	  
          state = AddOne();
	break;

      case kYellow:
           state = AddAll();
        break;

      case kBlue:
      case kOk:
           state = ID3Info();
	break;   

      default:
           state = osContinue;
    }
  }
  
  return state;
}        		


//---------------- cMP3RatingTo ---------------------------------------------------------------------------------------------------------------------

char *cMP3SearchRatingTo::ratingto = NULL;

cMP3SearchRatingTo::cMP3SearchRatingTo(void)
:cOsdMenu(tr("Rating to"))
{
  free(ratingto); ratingto=0;
  SetHelp();
  LoadItems();
}

cMP3SearchRatingTo::~cMP3SearchRatingTo()
{
}

void cMP3SearchRatingTo::SetHelp(void) {
    cOsdMenu::SetHelp(tr("Parent"),NULL,NULL,tr("Select"));
}

void cMP3SearchRatingTo::LoadItems(void)
{
  std::ifstream in;
  std::string Datei;
  std::string line;
  
  Datei = config;
  Datei = Datei + "/" + langdir;
  Datei = Datei + "/data/ratingsearchtext.dat";

  in.open (Datei.c_str());
  
  if(in) {
    while ((getline(in,line,'\n'))) {
      Add(new cOsdItem(hk(line.c_str())));
    }

    in.close();
  }

  Display();
}


void cMP3SearchRatingTo::GetValue(void)
{
  cOsdItem *item = (cOsdItem*)Get(Current());

  if(item) {
    ratingto = strdup(itoa(Current())); 
  }

}


eOSState cMP3SearchRatingTo::ProcessKey(eKeys Key)
{
  eOSState state = cOsdMenu::ProcessKey(Key);

  if (state == osUnknown) {
    switch (Key) {
        case kRed:
        case kBack: state = osBack; 
          break;

        case kOk:
	case kBlue:
            GetValue();
            state = osBack;
          break;

	case kGreen:
	case kYellow:

        default: state = osContinue;
    }
  }

  return state;
}


//---------------- cMP3RatingFrom ---------------------------------------------------------------------------------------------------------------------

char *cMP3SearchRatingFrom::ratingfrom = NULL;

cMP3SearchRatingFrom::cMP3SearchRatingFrom(void)
:cOsdMenu(tr("Rating from"))
{
  free(ratingfrom); ratingfrom=0;
  SetHelp();
  LoadItems();
}

cMP3SearchRatingFrom::~cMP3SearchRatingFrom()
{
}

void cMP3SearchRatingFrom::SetHelp(void) {
    cOsdMenu::SetHelp(tr("Parent"),NULL,NULL,tr("Select"));
}

void cMP3SearchRatingFrom::LoadItems(void)
{
  std::ifstream in;
  std::string Datei;
  std::string line;
  
  Datei = config;
  Datei = Datei + "/" + langdir;
  Datei = Datei + "/data/ratingsearchtext.dat";

  in.open (Datei.c_str());
  
  if(in) {
    while ((getline(in,line,'\n'))) {
      Add(new cOsdItem(hk(line.c_str())));
    }

    in.close();
  }

  Display();
}


void cMP3SearchRatingFrom::GetValue(void)
{
  cOsdItem *item = (cOsdItem*)Get(Current());

  if(item) {
    ratingfrom = strdup(itoa(Current()));
  }

}


eOSState cMP3SearchRatingFrom::ProcessKey(eKeys Key)
{
  eOSState state = cOsdMenu::ProcessKey(Key);

  if (state == osUnknown) {
    switch (Key) {
        case kRed:
        case kBack: state = osBack; 
          break;

        case kOk:
	case kBlue:
            GetValue();
            state = osBack;
          break;


	case kGreen:
	case kYellow:

        default: state = osContinue;
    }
  }

  return state;
}


//---------------- cMP3SearchGenre ---------------------------------------------------------------------------------------------------------------------

char *cMP3SearchGenre::genretext = NULL;

cMP3SearchGenre::cMP3SearchGenre(void)
:cOsdMenu(tr("Genre"))
{
  free(genretext); genretext=0;
  SetHelp();
  LoadItems();
}

cMP3SearchGenre::~cMP3SearchGenre()
{
}

void cMP3SearchGenre::SetHelp(void) {
    cOsdMenu::SetHelp(tr("Parent"),NULL,NULL,tr("Select"));
}


void cMP3SearchGenre::LoadItems(void)
{
  std::ifstream in;
  std::string Datei;
  std::string line;
  
  Datei = config;
  Datei = Datei + "/" + langdir;
  Datei = Datei + "/data/genre.dat";

  in.open (Datei.c_str());
  
  if(in) {
    while ((getline(in,line,'\n'))) {
      Add(new cOsdItem(hk(line.c_str())));
    }

    in.close();
  }

  Display();
}


void cMP3SearchGenre::GetValue(void)
{
  cOsdItem *item = (cOsdItem*)Get(Current());

  if(item) {
   genretext = strdup(item->Text()); 
  }

}


eOSState cMP3SearchGenre::ProcessKey(eKeys Key)
{
  eOSState state = cOsdMenu::ProcessKey(Key);

  if (state == osUnknown) {
    switch (Key) {
        case kRed:
        case kBack: state = osBack; 
          break;

        case kOk:
	case kBlue:
            GetValue();
            state = osBack;
          break;

	case kGreen:
	case kYellow:

        default: state = osContinue;
    }
  }

  return state;
}


//---------------- cMP3Search --------------------------------------------------------------------------------------------------------------------------

cMP3Search::cMP3Search(const char *Name)
:cOsdMenu(tr("Music: Search"),20)
{
  strncpy(Artist, Name, sizeof(Artist));
  updateGenre=updateRatingFrom=updateRatingTo=false;
  UpdateMenu(5);
}

cMP3Search::~cMP3Search()
{
}

void cMP3Search::ShowDisplay(void)
{
  
  Clear();
      
  Add(new cOsdItem(hk(tr("Load previous query"))));
  Add(new cOsdItem(hk(tr("Reset query"))));
  Add(new cOsdItem(hk(tr("Execute query"))));
  Item("");
  Add(new cMenuEditStrItem(tr("Artist")          ,Artist      , sizeof(Artist)-1 ));
  Add(new cMenuEditStrItem(tr("Title")           ,Title       , sizeof(Title)-1  ));
  Add(new cMenuEditStrItem(tr("Genre")           ,Genre       , sizeof(Genre)-1  ));
  Add(new cMenuEditStrItem(tr("Filename")        ,FullFile    , sizeof(FullFile)-1 ));
  Add(new cMenuEditIntItem(tr("Year from")       ,&YearFrom   , -1     , 2030));
  Add(new cMenuEditIntItem(tr("Year to")         ,&YearTo     , -1     , 2030));
  Add(new cMenuEditIntItem(tr("Rating from")     ,&RatingFrom , -1     , 10));
  Add(new cMenuEditIntItem(tr("Rating to")       ,&RatingTo   , -1     , 10));
  Item("");
  Add(new cMenuEditBoolItem(tr("Only available songs") ,&Exist      ,tr("no"), tr("yes")));

  SetCurrent(Get(4));
  SetHelp();
  Display();
}


void cMP3Search::SetHelp()
{
  cOsdMenu::SetHelp(tr("Parent"),NULL,NULL,NULL);
}


cOsdItem *cMP3Search::Item(const char *text)
{
  char *buf=0;
  asprintf(&buf,"%s",text?text:"");
  cOsdItem *item = new cOsdItem(buf,osBack);
  item->SetSelectable(false);
  free(buf);
  Add(item); return item;
}



void cMP3Search::SaveSearch(void) {
  std::string Datei;
  
  Datei = BaseSource;
  Datei = Datei + "/@Search.bak";
  
  if(FILE* fd=fopen(Datei.c_str(), "w")) {
    char *query;
    asprintf(&query, "%s\n%s\n%s\n%s\n%d\n%d\n%d\n%d\n%d",Artist,Title,Genre,FullFile,YearFrom,YearTo,RatingFrom,RatingTo,Exist);
    fprintf(fd, query);
    d(printf("music: search: Save query '%s'\n",query));
    free(query);
    
    fclose(fd);
  }    
}


eOSState cMP3Search::Searching(void) {

  if(HasSubMenu()) return osContinue;

  Skins.Message(mtStatus,tr("Searching..."));

  SaveSearch();    

  if(InfoCache.SearchUp(Artist, Title , Genre, FullFile, YearFrom, YearTo, RatingFrom, RatingTo, Exist)) {
    char *buf;
    asprintf(&buf,"%s %i", tr("Found:"), InfoCache.expr1);
    d(printf("music: search: %s of %i\n", buf, InfoCache.expr2));
    strncpy(searchresult,buf,sizeof(searchresult));
    free(buf);
    Skins.Message(mtStatus,NULL);
    return AddSubMenu(new cMP3SearchResult(searchresult));
    }
  else {
    Skins.Message(mtStatus,NULL);
    return AddSubMenu(new cMenuText(tr("ERROR:"), tr("ERROR: Could not find anything !"), fontFix));
  }

  return osContinue;
}


void cMP3Search::UpdateMenu(int Which) {

  int which = Which;

  switch(which) {
    case 0: // ClearValues
      d(printf("music: search: clear values\n"));
      ClearValues();    
      break;

    case 1: // PreviousSearch
      d(printf("music: search: load previous search\n"));
      PreviousSearch();    
      break;

    case 2: // Get Genretest
      d(printf("music: search: update value for genre\n"));
      updateGenre = false;
      SetGenre();           
      break;

    case 3: // Get RatingFrom
      d(printf("music: search: update value for rating from\n"));
      updateRatingFrom = false;
      SetRatingFrom();
      break;

    case 4: // Get RatingTo
      d(printf("music: search: update value for rating to\n"));
      updateRatingTo = false;
      SetRatingTo();
      break;

    case 5: // First Search
      d(printf("music: search: first search\n"));
      NewValues();    
      break;


    default:
      ClearValues();
  }    

}


void cMP3Search::PreviousSearch(void) {

  std::ifstream in;
  std::string line;
  std::string pDatei;
  std::string pArtist;
  std::string pTitle;
  std::string pGenre;
  std::string pFullFile;
  std::string pYearFrom;
  std::string pYearTo;
  std::string pRankingFrom;
  std::string pRankingTo;
  std::string pPlayable;
  
  pDatei = BaseSource;
  pDatei = pDatei + "/@Search.bak";

  int i = 0;

  in.open(pDatei.c_str());
  if(in) {
    while (getline(in, line, '\n')) {

      if(i==0) pArtist=line;
       else
      if(i==1) pTitle=line;
       else
      if(i==2) pGenre=line;
       else
      if(i==3) pFullFile=line;
       else
      if(i==4) pYearFrom=line;
       else
      if(i==5) pYearTo=line;
       else
      if(i==6) pRankingFrom=line;
       else
      if(i==7) pRankingTo=line;
       else
      if(i==8) pPlayable=line;

      i++;
    }

  in.close();
  }    

  strncpy(Artist   , pArtist.c_str()   ,sizeof(Artist));
  strncpy(Title    , pTitle.c_str()    ,sizeof(Title));
  strncpy(Genre    , pGenre.c_str()    ,sizeof(Genre));
  strncpy(FullFile , pFullFile.c_str() ,sizeof(FullFile));
  YearFrom        = atoi(pYearFrom.c_str());
  YearTo          = atoi(pYearTo.c_str());
  RatingFrom      = atoi(pRankingFrom.c_str());
  RatingTo        = atoi(pRankingTo.c_str());
  Exist           = atoi(pPlayable.c_str());

  ShowDisplay();
}


void cMP3Search::NewValues(void) {

//  strncpy(Artist   ,"" ,sizeof(Artist));
  strncpy(Title    ,"" ,sizeof(Title));
  strncpy(Genre    ,"" ,sizeof(Genre));
  strncpy(FullFile ,"" ,sizeof(FullFile));
  YearFrom        =-1;
  YearTo          =-1;
  RatingFrom      =-1;
  RatingTo        =-1;
  Exist           =0;

  ShowDisplay();
}
    

void cMP3Search::ClearValues(void) {

  strncpy(Artist   ,"" ,sizeof(Artist));
  strncpy(Title    ,"" ,sizeof(Title));
  strncpy(Genre    ,"" ,sizeof(Genre));
  strncpy(FullFile ,"" ,sizeof(FullFile));
  YearFrom        =-1;
  YearTo          =-1;
  RatingFrom      =-1;
  RatingTo        =-1;
  Exist           =0;

  ShowDisplay();
}


void cMP3Search::SetGenre(void) {

  const char *genretext=cMP3SearchGenre::GenreText();
  if(genretext) {
    strncpy(Genre    ,genretext ,sizeof(Genre));
  }

  ShowDisplay();
}


void cMP3Search::SetRatingFrom(void) {

  const char *newrating=cMP3SearchRatingFrom::RatingFrom();
  if(newrating) {
    RatingFrom = atoi(newrating);
  }

  ShowDisplay();
}


void cMP3Search::SetRatingTo(void) {

  const char *newrating=cMP3SearchRatingTo::RatingTo();
  if(newrating) {
    RatingTo = atoi(newrating);
  }    

  ShowDisplay();
}

eOSState cMP3Search::ProcessKey(eKeys Key)
{
  int current = Current();

  eOSState state = cOsdMenu::ProcessKey(Key);

  if(!HasSubMenu() && state==osContinue) {
    if(updateGenre) UpdateMenu(2);
      else if(updateRatingFrom) UpdateMenu(3);
        else if(updateRatingTo)   UpdateMenu(4);
  }

  if (state == osUnknown) {
    switch (Key) {
        case kRed:     
        case kBack: state = osBack; 
          break;

        case kOk:
	  if(current ==0) {
            UpdateMenu(1);
	    state = osContinue;
	    }
	  else if(current==1) {
	    UpdateMenu(0);
	    state = osContinue;
	    }
	  else if(current ==2) {
	    state = Searching();
	    } 
	  else if(current ==6) {
            updateGenre = true;
	    return AddSubMenu(new cMP3SearchGenre()); 
	    } 
	  else if(current ==10) {
            updateRatingFrom = true;
	    return AddSubMenu(new cMP3SearchRatingFrom()); 
	    } 
	  else if(current ==11) {
            updateRatingTo = true;
	    return AddSubMenu(new cMP3SearchRatingTo()); 
	    } 
	  else         
	    state = Searching();
	  break;

	case kGreen:
	case kYellow:
        default: state = osContinue;
    }
  }

  return state;
}


