/*
 * cdda_menu.c: The menu implementations
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: cdda_menu.c,v 1.1.1.5 2005/06/27 13:34:02 cm Exp $
 */

#include <vdr/tools.h>
#include "cdda_conf.h"
#include "cdda_menu.h"
#include "cdda_player.h"
#include "cdda_playlist.h"

extern cCddaConfiguration oConfig;
extern cCddaDevice *po_Device;

// --- cCddaMenu ---------------------------------------------------------------
cCddaMenu::cCddaMenu():cOsdMenu(tr("CD Player"), 3, 34, 3, 0) {
  po_playlist = new cCddaPlaylist();
  po_playlist->RefIncr();

  po_playlist->SetType(ptAll);

  if (NULL == po_Device)
    po_Device = new cCddaDevice();
  po_Device->RefIncr();

  Initialize();
  Set();
}

cCddaMenu::~cCddaMenu() {
  po_playlist->RefDecr();
  if (0 == po_playlist->RefCount()) {
    po_playlist->Clear();
    DELETENULL(po_playlist);
  }

  po_Device->RefDecr();
  if (0 == po_Device->RefCount()) {
    po_Device->Close();
    DELETENULL(po_Device);
  }
}

void cCddaMenu::Initialize(void) {
  Skins.Message(mtStatus, tr("Loading disc information"));
  Display();

  if (! po_Device->isOpen())
    po_Device->Open(oConfig.GetDevice());

  if (po_Device->isOpen() && po_Device->Disc()->isAudio()) {
    if (cpOff != oConfig.GetCddbPriority())
      if (NULL == po_Device->Disc()->Cdtext()->GetTitle() || cpAlways == oConfig.GetCddbPriority())
        po_Device->Disc()->ReadCddb(oConfig.GetCddbServerFqdn(),
                                    oConfig.GetCddbServerPort(),
                                    oConfig.allowCddbCache() ? oConfig.GetCddbDir() : NULL);
  }
}

void cCddaMenu::Set() {
  int min = 0, sec = 0, current = Current();
  unsigned int i = 0;
  char *p_text = NULL;
  const char *p_performer = NULL;
  const char *p_title = NULL;
  cCddaTracks *po_tracks = NULL;

  Clear();
  
  if (po_Device->isOpen() && po_Device->Disc()->isAudio()) {
    po_tracks = po_Device->Disc()->Tracks();
    for (i = po_tracks->GetFirstNum(); i <= po_tracks->GetNumTracks(); i++) {
      p_performer = po_tracks->Item(i)->Cdtext()->GetPerformer();
      p_title = po_tracks->Item(i)->Cdtext()->GetTitle();
      sec = POSITIONTOSECONDS(po_tracks->Item(i)->GetLength()) % 60;
      min = POSITIONTOSECONDS(po_tracks->Item(i)->GetLength()) / 60;
      asprintf(&p_text, "%2d\t%s - %s\t%s\t%2d:%02d",
               i,
               p_performer ? p_performer : tr("Artist"),
               p_title ? p_title : tr("Track"),
               NULL == po_playlist->GetByKey(i) ? "" : "*",
               min,
               sec);
      Add(new cOsdItem(p_text));
      free(p_text);
    }
    SetHelp(ptProgram == po_playlist->GetType() ? tr("Commands") : NULL ,
            ptAll == po_playlist->GetType() ? tr("All") : (ptSingle == po_playlist->GetType() ? tr("Single") : tr("Program")),
            tr("Eject"),
            tr("Information"));
  } else {
    cOsdItem *po_OsdItem = new cOsdItem(tr("No music cd in your drive"));
    po_OsdItem->SetSelectable(false);
    Add(po_OsdItem);
    SetHelp(NULL, NULL, tr("Refresh"), NULL);
  }

  SetCurrent(Get(current));

  Display();
}

eOSState cCddaMenu::ProcessKey(eKeys Key) {
  static int track = 0;
  static int ticks = 0;
  static int lastNumTracks = po_playlist->GetNumTracks();
  int current = Current();
  bool dirty = false;
  char buffer[64];

  eOSState state = cOsdMenu::ProcessKey(Key);

  if (state == osUnknown && !HasSubMenu()) {
    if (ticks) {
      ticks++;

      if (5 == ticks) {
        po_playlist->Load(track, po_Device->Disc()->Tracks()->GetFirstNum(), po_Device->Disc()->Tracks()->GetNumTracks());
        cControl::Launch(new cCddaControl(po_playlist));

        state = osEnd;
        track = ticks = 0;
      }
    }

    switch (Key & ~k_Repeat) {
      case kOk:
        if (! (po_Device->isOpen() && po_Device->Disc()->isAudio()))
	  break;
	  
        po_playlist->Load(current+1, po_Device->Disc()->Tracks()->GetFirstNum(), po_Device->Disc()->Tracks()->GetNumTracks());
        cControl::Launch(new cCddaControl(po_playlist));

        state = osEnd;
        break;
      case kBack:
        state = osBack;
        break;
      case kRed:
        if (! (po_Device->isOpen() && po_Device->Disc()->isAudio()))
	  break;
	
        if (ptProgram == po_playlist->GetType())
          state = AddSubMenu(new cCddaMenuCommands(po_playlist, current+1));

        break;
      case kGreen:
        if (! (po_Device->isOpen() && po_Device->Disc()->isAudio()))
	  break;
	
        if (ptAll == po_playlist->GetType())
          po_playlist->SetType(ptSingle);
        else if (ptSingle == po_playlist->GetType())
          po_playlist->SetType(ptProgram);
        else if (ptProgram == po_playlist->GetType())
          po_playlist->SetType(ptAll);

        po_playlist->Clear();
        dirty = true;
        break;
      case kYellow:
        if (po_Device->isOpen()) {
          // check if device is already opened and no one other is accessing our po_Device (e.g. the player)
          if (1 == po_Device->RefCount()) {
            Clear();
            SetHelp(NULL, NULL, tr("Refresh"), NULL);
            Display();
            po_Device->Eject();
            po_Device->Close();
          } else
            SetStatus(tr("Cannot eject disc while replaying"));
        } else
          Initialize();

        dirty = true;
        break;
      case kBlue:
        if (! (po_Device->isOpen() && po_Device->Disc()->isAudio()))
	  break;
      
        state = AddSubMenu(new cCddaMenuTrackInfo(tr("Information"),
                                                  po_Device->Disc(),
                                                  po_Device->Disc()->Tracks()->Item(current+1)));
        break;
      case k0 ... k9:
        if (! (po_Device->isOpen() && po_Device->Disc()->isAudio()))
	  break;
	
        if (! ticks)
          ticks++;

        if (! track) {
          track = (Key & ~k_Repeat) - k0;
          snprintf(buffer, 64, "%s - %s %d-", tr("CD Player"), tr("Jump to"), track);
          SetStatus(buffer);
        } else {
          track = track*10 + (Key & ~k_Repeat) - k0;

          po_playlist->Load(track, po_Device->Disc()->Tracks()->GetFirstNum(), po_Device->Disc()->Tracks()->GetNumTracks());
          cControl::Launch(new cCddaControl(po_playlist));

          state = osEnd;
          track = ticks = 0;
        }

        break;
      default:
        break;
      } // switch (Key ...
    if (dirty)
      Set();
  } // if (state == ...

  if (lastNumTracks != po_playlist->GetNumTracks()) {
    Set();
    lastNumTracks = po_playlist->GetNumTracks();
  }

  if (po_Device->isOpen())
    if (po_Device->Disc()->isCdtextDirty()) {
      po_Device->Disc()->SetCdtextDirty(false);
      Set();
    }

  return state;
}

// --- cCddaMenuCommands -------------------------------------------------------

cCddaMenuCommands::cCddaMenuCommands(cCddaPlaylist *playlist, unsigned int track) : cOsdMenu(tr("Commands")){
  selectedTrack = track;

  po_playlist = playlist;
  po_playlist->RefIncr();

  Set();
}

cCddaMenuCommands::~cCddaMenuCommands() {
  po_playlist->RefDecr();
  po_playlist = NULL;
}

void cCddaMenuCommands::Set() {
  SetHasHotkeys();

  if (NULL == po_playlist->GetByKey(selectedTrack))
    Add(new cOsdItem(hk(tr("Add to playlist")), osUser1));
  else
    Add(new cOsdItem(hk(tr("Remove from playlist")), osUser2));
}

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

  switch (state) {
    case osUser1:
      po_playlist->Add()->SetKey(selectedTrack);
      state = osBack;
      break;
    case osUser2:
      po_playlist->Remove(po_playlist->GetByKey(selectedTrack)->Index());
      state = osBack;
      break;
    default:
      break;
  }

  return state;
}

// --- cCddaMenuTrackInfo ------------------------------------------------------

cCddaMenuTrackInfo::cCddaMenuTrackInfo(const char *Title, cCddaDisc *disc, cCddaTrack *track) : cOsdMenu(Title, 13) {
  Set(disc, track);
}

cCddaMenuTrackInfo::~cCddaMenuTrackInfo() {
}

const char *cCddaMenuTrackInfo::ConcatStrings(const char *desc, const char *text, bool indent) {
  static char str[81];

  memset(str, 0x00, 81);
  snprintf(str, 81, "%s%s:\t%s", indent ? "  " : "", desc, text ? text : "-");

  return str;
}

void cCddaMenuTrackInfo::Set(cCddaDisc *disc, cCddaTrack *track) {
  cOsdItem *po_item = NULL;

  Add(po_item = new cOsdItem(tr("Disc Information")));
  po_item->SetSelectable(false);
  Add(po_item = new cOsdItem(ConcatStrings(tr("Artist"), disc->Cdtext()->GetPerformer(), true)));
  po_item->SetSelectable(false);
  Add(po_item = new cOsdItem(ConcatStrings(tr("Album"), disc->Cdtext()->GetTitle(), true)));
  po_item->SetSelectable(false);
  Add(po_item = new cOsdItem(ConcatStrings(tr("Disc ID"), disc->Cdtext()->GetDiscId(), true)));
  po_item->SetSelectable(false);

  Add(po_item = new cOsdItem(tr("Track information")));
  po_item->SetSelectable(false);
  Add(po_item = new cOsdItem(ConcatStrings(tr("Arranger"), track->Cdtext()->GetArranger(), true)));
  po_item->SetSelectable(false);
  Add(po_item = new cOsdItem(ConcatStrings(tr("Composer"), track->Cdtext()->GetComposer(), true)));
  po_item->SetSelectable(false);
  Add(po_item = new cOsdItem(ConcatStrings(tr("Songwriter"), track->Cdtext()->GetSongwriter(), true)));
  po_item->SetSelectable(false);
  Add(po_item = new cOsdItem(ConcatStrings(tr("Artist"), track->Cdtext()->GetPerformer(), true)));
  po_item->SetSelectable(false);
  Add(po_item = new cOsdItem(ConcatStrings(tr("Title"), track->Cdtext()->GetTitle(), true)));
  po_item->SetSelectable(false);
  Add(po_item = new cOsdItem(ConcatStrings(tr("Genre"), track->Cdtext()->GetGenre(), true)));
  po_item->SetSelectable(false);
  Add(po_item = new cOsdItem(ConcatStrings(tr("Message"), track->Cdtext()->GetMessage(), true)));
  po_item->SetSelectable(false);

  Display();
}

// --- cCddaSetupMenu ----------------------------------------------------------
cCddaSetupMenu::cCddaSetupMenu() {
  newMenuEntry = oConfig.allowMenuEntry();
  newSkipSeconds= oConfig.GetSkipSeconds();
  newAudioOnly = oConfig.isAudioOnly();
  newSkipBackMargin = oConfig.GetSkipBackMargin();
  newAutoReplay = oConfig.isAutoReplay();
  newCddbPriority = oConfig.GetCddbPriority();
  p_newCddbServerFqdn = oConfig.GetCddbServerFqdn();
  newCddbServerPort = oConfig.GetCddbServerPort();
  newCddbCache = oConfig.allowCddbCache();
  newInfoView = oConfig.GetDisplayInfoView();

  cddbPriorityTexts[0] = tr("off");
  cddbPriorityTexts[1] = tr("no cd-text");
  cddbPriorityTexts[2] = tr("always");

  Add(new cMenuEditBoolItem(tr("Mainmenu entry"), &newMenuEntry, tr("hide"), tr("show")));
  Add(new cMenuEditBoolItem(tr("Information view"), &newInfoView, tr("Track"), tr("All")));
  Add(new cMenuEditIntItem(tr("Skip length (s)"), &newSkipSeconds, 1, 15));
  Add(new cMenuEditIntItem(tr("Skip back margin (s)"), &newSkipBackMargin, 1, 15));
  Add(new cMenuEditBoolItem(tr("Background"), &newAudioOnly, tr("black"), tr("live")));
  Add(new cMenuEditBoolItem(tr("Autostart replay"), &newAutoReplay));
  Add(new cMenuEditStraItem(tr("CDDB Priority"), &newCddbPriority, 3, cddbPriorityTexts));
  Add(new cMenuEditStrItem(tr("CDDB Server FQDN"), p_newCddbServerFqdn, 32, "abcdefghijklmnopqrstuvxyz.-"));
  Add(new cMenuEditIntItem(tr("CDDB Server Port"), &newCddbServerPort, 1, 65535));
  Add(new cMenuEditBoolItem(tr("CDDB Cache"), &newCddbCache, tr("off"), tr("on")));
}

void cCddaSetupMenu::Store(void) {
  SetupStore("menuEntry", oConfig.SetMenuEntry(newMenuEntry));
  SetupStore("skipSeconds", oConfig.SetSkipSeconds(newSkipSeconds));
  SetupStore("skipBackMargin", oConfig.SetSkipBackMargin(newSkipBackMargin));
  SetupStore("audioOnly", oConfig.SetAudioOnly(newAudioOnly));
  SetupStore("autoReplay", oConfig.SetAutoReplay(newAutoReplay));
  SetupStore("cddbPriority", oConfig.SetCddbPriority((eCddbPriority)newCddbPriority));
  SetupStore("cddbServerFqdn", oConfig.SetCddbServerFqdn(p_newCddbServerFqdn));
  SetupStore("cddbServerPort", oConfig.SetCddbServerPort(newCddbServerPort));
  SetupStore("cddbCache", oConfig.SetCddbCache(newCddbCache));
  SetupStore("infoView", oConfig.SetDisplayInfoView((eDisplayInformationView)newInfoView));
}
