/*
 * audiocd.c: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: audiocd.c 1.11 2002/12/13 15:01:43 kls Exp $
 */

#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
#include <typeinfo>
#include <vdr/plugin.h>
#include <vdr/player.h>
#include <vdr/interface.h>
#include <vdr/skins.h>
#include <cdaudio.h>
#include "i18n.h"

#define MAX_TRACKS 100

static const char *VERSION        = "0.0.6a";
static const char *DESCRIPTION    = "An AudioCD Player";
static const char *MAINMENUENTRY  = "AudioCD";

#define clrBackground clrGray50
#define clrForeground clrWhite
#define eDvbColor int

// --- cACDPlayer -------------------------------------------------------------

enum eRepeatMode {
  rmDisabled,
  rmTrack,
  rmDisc
};

class cACDPlayer : public cPlayer {
  protected:
    cOsd *osd;
    int bw, bh, bwc, fw, fh;

    const cFont *font;

    bool  fbVisible;
    int   fiDevice;
    enum eRepeatMode feRepeatMode;
    bool  fbShuffle;
    int   fiTrackSelected;
    char  * cdArtist;
    char  * cdTitle;
    char  * cdTrack[ MAX_TRACKS ];
    int   numTracks;
    void GetInfo();
    void ClearInfo();
    void Fill(int x, int y, int w, int h, eDvbColor fg);
    void Write(int x, int y, const char *text, eDvbColor fg=clrForeground, eDvbColor bg=clrBackground);

  public:
    cACDPlayer(char *sDevice);
    ~cACDPlayer();	
    eOSState ProcessKey(eKeys eKey);
    void InterfaceUpdate();
    void Hide();
};

cACDPlayer::cACDPlayer(char *sDevice)
  : cPlayer(pmAudioOnlyBlack)
{
//  dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
  osd=0;
  font=cFont::GetFont(fontOsd);
  fiDevice = cd_init_device(sDevice);
  fbVisible = true;
  feRepeatMode = rmDisabled;
  fbShuffle = false;
  fiTrackSelected = 0;
  GetInfo();

  struct disc_volume dv;
  dv.vol_front.right = dv.vol_front.left = 255;
  dv.vol_back.right = dv.vol_back.left = 255;
  cd_set_volume(fiDevice, dv);

  ProcessKey(kPlay);
}

cACDPlayer::~cACDPlayer()
{
//  dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
  Hide();
  cd_stop(fiDevice);
  cd_finish(fiDevice);

}

void cACDPlayer::Fill(int x, int y, int w, int h, eDvbColor fg)
{
  //d(printf("fill x=%d y=%d w=%d h=%d ->",x,y,w,h))
  x*=fw; if(x<0) x+=bw;
  y*=fh; if(y<0) y+=bh;
  osd->DrawRectangle(x,y,x+w*fw-1,y+h*fh-1,fg);
  //d(printf(" x=%d y=%d x2=%d y2=%d\n",x,y,x+h*fh-1,y+w*fw-1))
}

void cACDPlayer::Write(int x, int y, const char *text, eDvbColor fg, eDvbColor bg)
{
  int w = strlen( text );
  //d(printf("write x=%d y=%d w=%d ->",x,y,w))
  x*=fw; if(x<0) x+=bw;
  y*=fh; if(y<0) y+=bh;
  osd->DrawText(x,y,text,fg,bg,font,w*fw);
  //d(printf(" x=%d y=%d w=%d\n",x,y,w*fw))
}


void cACDPlayer::InterfaceUpdate()
{
  if (!fbVisible)
    return;

//  dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);

  struct disc_info di;
  cd_stat(fiDevice, &di);

  if (!osd)
  {
//    static int cols[] = { 5, 0 };
    int bt, bp;
    fw=font->Width(' ')*2;
    fh=font->Height();
    bw=Setup.OSDWidth;
    bh=bp=2*fh;
    bt=Setup.OSDHeight-bh;

    bwc=bw/fw+1;
    //d(printf("mp3: bw=%d bh=%d bt=%d bp=%d bwc=%d rows=%d fw=%d fh=%d\n",
    //  bw,bh,bt,bp,bwc,rows,fw,fh))
    osd=cOsdProvider::NewOsd(Setup.OSDLeft,Setup.OSDTop+bt);
    if(!osd) return;
    tArea Areas[] = { { 0,0,bw-1,bh-1,4 } };
    osd->SetAreas(Areas,sizeof(Areas)/sizeof(tArea));
    osd->DrawRectangle(0,0,bw-1,bh-1,clrGray50);
    osd->Flush();
//    Interface->Open(Setup.OSDwidth, -4);
//    Interface->SetCols(cols);
//    Interface->Clear();
  }

  char *buf = 0;
  Fill(0, -4, 80, 1, clrBackground);
  Fill(0, -3, 80, 1, clrBackground);
  Fill(0, -2, 80, 1, clrBackground);
  if (di.disc_present)
  {
    if( cdArtist != NULL )
    {
       Write(0, -4, cdArtist);
    }
    if( cdTitle != NULL )
    {
       Write(0, -3, cdTitle);
    }
    if (di.disc_mode == CDAUDIO_PLAYING)
    {
       if( di.disc_current_track <= numTracks )
       {
          asprintf(&buf, "(%d/%d) %s", di.disc_current_track, di.disc_total_tracks, cdTrack[ di.disc_current_track - 1 ] );
       }
       else
          asprintf(&buf, "(%d/%d)", di.disc_current_track, di.disc_total_tracks);
    }
    else
      asprintf(&buf, "(-/%d)", di.disc_total_tracks);
  }
  else
    asprintf(&buf, "(-/-)");

  Write(0, -2, buf);
  free(buf);

  buf = 0;
  switch (feRepeatMode)
  {
    case rmDisabled: asprintf(&buf, " "); break;
    case rmDisc:     asprintf(&buf, "D"); break;
    case rmTrack:    asprintf(&buf, "T"); break;
    default:         break;
  }
  if (buf)
  {
    Write(-4, -1, buf, clrBlack, clrGreen);
    free(buf);
  }

  if (fbShuffle)
    Write(-2, -1, "S", clrBlack, clrYellow);
  else
    Write(-2, -1, " ", clrBlack, clrYellow);

  osd->Flush();
}

eOSState cACDPlayer::ProcessKey(eKeys eKey)
{
//  dsyslog("%s: %s(%d)", __FILE__, __PRETTY_FUNCTION__, eKey);
  eOSState st = osContinue;
  struct disc_timeval dt;
  memset(&dt, 0, sizeof(dt));
  struct disc_info di;
  cd_stat(fiDevice, &di);

  switch (eKey)
  {
    case kRight:
    case kRight | k_Repeat:
    case kFastFwd:
    case kFastFwd | k_Repeat:
      if (di.disc_present && di.disc_mode == CDAUDIO_PLAYING)
      {
        dt.seconds = 5;
        cd_advance(fiDevice, dt);
      }
      break;

    case kLeft:
    case kLeft | k_Repeat:
    case kFastRew:
    case kFastRew | k_Repeat:
      if (di.disc_present && di.disc_mode == CDAUDIO_PLAYING)
      {
        dt.seconds = -5;
        cd_advance(fiDevice, dt);
      }
      break;

    case kUp:
    case kUp | k_Repeat:
      if (di.disc_present && di.disc_mode == CDAUDIO_PLAYING)
      {
        if (di.disc_current_track < di.disc_total_tracks)
          di.disc_current_track++;
        cd_play(fiDevice, di.disc_current_track);
      }
      break;

    case kDown:
    case kDown | k_Repeat:
      if (di.disc_present && di.disc_mode == CDAUDIO_PLAYING)
      {
        if (di.disc_current_track > di.disc_first_track)
          di.disc_current_track--;
        cd_play(fiDevice, di.disc_current_track);
      }
      break;

    case kPlay:
    case kPause:
      if (!di.disc_present)
        break;
      switch (di.disc_mode)
      {
        case CDAUDIO_PLAYING:
          if (eKey == kPlay)
            cd_play(fiDevice, di.disc_current_track);
          else
            cd_pause(fiDevice);
          break;
        case CDAUDIO_PAUSED:
          cd_resume(fiDevice);
          break;
        default:
          if (eKey == kPlay)
            cd_play(fiDevice, di.disc_first_track);
          break;
      }
      break;

    case kGreen:
      switch (feRepeatMode)
      {
        case rmDisabled: feRepeatMode = rmTrack ; break;
        case rmTrack:    feRepeatMode = rmDisc; break;
        case rmDisc:     feRepeatMode = rmDisabled; break;
        default:         break;
      }
      break;

    case kYellow:
      fbShuffle = !fbShuffle;
      break;

    case k0 ... k9:
      if (!di.disc_present || di.disc_mode != CDAUDIO_PLAYING)
        break;
      fiTrackSelected = fiTrackSelected * 10 + eKey - k0;
      break;

    case kNone:
      if (fiTrackSelected)
      {
        if (fiTrackSelected < di.disc_total_tracks &&
            di.disc_track[fiTrackSelected].track_type == CDAUDIO_TRACK_AUDIO)
          cd_play(fiDevice, fiTrackSelected);
        fiTrackSelected = 0;
      }	
      break;

    case kOk:
      fbVisible = !fbVisible;	
      if (!fbVisible)
        Hide();
      break;
	
    default:
      st = osUnknown;
      break;
  }

  if (st == osContinue)
    InterfaceUpdate();

  return st;
}

void cACDPlayer::Hide()
{
  if (fbVisible)
  {
    delete osd; osd=0;
    fbVisible = false;
  }
}

void cACDPlayer::GetInfo()
{
   FILE *fhdl = fopen( "/tmp/audio_cd", "r" );
   static char buffer[ 2048 ];

   cdArtist = NULL;
   cdTitle = NULL;

   if( fhdl != NULL )
   {
      if ( fgets( buffer, sizeof( buffer ), fhdl ) != NULL )
      {
         cdArtist = ( char * )malloc( strlen( buffer ) );
         strcpy( cdArtist, buffer );
         printf( "Artist: %s\n", cdArtist );
      }
      if ( fgets( buffer, sizeof( buffer ), fhdl ) != NULL )
      {
         cdTitle = ( char * )malloc( strlen( buffer ) );
         strcpy( cdTitle, buffer );
         printf( "Title: %s\n", cdArtist );
      }


      numTracks = 0;
      while( fgets( buffer, sizeof( buffer ), fhdl ) != NULL )
      {
         if( strlen( buffer ) > 1 )
         {
            cdTrack[ numTracks ] = ( char * )malloc( strlen( buffer ) + 1 );
            strcpy( cdTrack[ numTracks ], buffer );
            numTracks++;
            printf( "%d: %s\n", numTracks, cdTrack[ numTracks - 1 ] );
         }
      }
      fclose( fhdl );
   }
}

void cACDPlayer::ClearInfo()
{
   int i;

   if ( cdArtist != NULL )
   {
      free( cdArtist );
   }
   if ( cdTitle != NULL )
   {
      free( cdTitle );
   }

   for( i = 0; i < numTracks; i++ )
   {
      if ( cdTrack[ i ] != NULL )
      {
         free( cdTrack[i] );
      }
   }
}

// --- cACDControl ------------------------------------------------------------

class cACDControl : public cControl {
  protected:
   cACDPlayer *fACDPlayer;

  public:
   cACDControl(char *sDevice);
   ~cACDControl();
   void Hide();
   void Stop();
   eOSState ProcessKey(eKeys eKey);
   static void Play(char *sDevice);
};

cACDControl::cACDControl(char *sDevice)
   : cControl(fACDPlayer = new cACDPlayer(sDevice))
{
//  dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
}

cACDControl::~cACDControl()
{
//  dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
  Hide();
  Stop();
}

void cACDControl::Hide()
{
//  dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
  fACDPlayer->Hide();
}

void cACDControl::Stop()
{
//  dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
  delete fACDPlayer;
  fACDPlayer = 0;
}

eOSState cACDControl::ProcessKey(eKeys eKey)
{
//  dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);

  switch (eKey)
  {
    case kBlue:
    case kStop:
    case kBack:
      return osEnd;

    default:
      break;
  }

  return fACDPlayer->ProcessKey(eKey);
}

void cACDControl::Play(char *sDevice)
{
//  dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
  cControl *control = cControl::Control();
  if (control && typeid(*control) == typeid(cACDControl))
    return;
  cControl::Launch(new cACDControl(sDevice));
}

// --- cPluginAudioCD ---------------------------------------------------------

class cPluginAudioCD : public cPlugin {
  private:
   char *sDevice;
   cACDControl *acdcControl;

  public:
   cPluginAudioCD(void);
   virtual ~cPluginAudioCD();
   virtual const char *Version(void) { return VERSION; }
   virtual const char *Description(void) { return tr(DESCRIPTION); }
   virtual const char *CommandLineHelp(void);
   virtual bool ProcessArgs(int argc, char *argv[]);
   virtual bool Start(void);
   virtual const char *MainMenuEntry(void) { return tr(MAINMENUENTRY); }
   virtual cOsdObject *MainMenuAction(void);
   virtual cMenuSetupPage *SetupMenu(void);
   virtual bool SetupParse(const char *Name, const char *Value);
};

cPluginAudioCD::cPluginAudioCD(void)
{
//   dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
   sDevice = "/dev/cdrom";
   acdcControl = NULL;
}

cPluginAudioCD::~cPluginAudioCD()
{
   dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
}

const char *cPluginAudioCD::CommandLineHelp(void)
{
//   dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
   return "  -d dev    --device=dev   set device (default: /dev/cdrom)\n";
}

bool cPluginAudioCD::ProcessArgs(int argc, char *argv[])
{
   dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
   static struct option long_options[] = {
      { "device",      required_argument, NULL, 'd' },
      { NULL }
   };

   int c;
   while ((c = getopt_long(argc, argv, "d:", long_options, NULL)) != -1) {
      switch (c) {
	 case 'd': sDevice = optarg;
	    break;
	 default:  return false;
      }
   }
   return true;
}

bool cPluginAudioCD::Start(void)
{
//   dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
   RegisterI18n(Phrases);
   return true;
}

cOsdObject *cPluginAudioCD::MainMenuAction(void)
{
//   dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
   cACDControl::Play(sDevice);
   return 0;
}

cMenuSetupPage *cPluginAudioCD::SetupMenu(void)
{
//   dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
   return NULL;
}

bool cPluginAudioCD::SetupParse(const char *Name, const char *Value)
{
//   dsyslog("%s: %s", __FILE__, __PRETTY_FUNCTION__);
   return false;
}

VDRPLUGINCREATOR(cPluginAudioCD); // Don't touch this!
