#ifndef __ALCD_THREAD_CPP
#define __ALCD_THREAD_CPP

#include "alcd-thread.h"
#include <vdr/player.h>
#include <vdr/menu.h>
#include <math.h>
#include "i18n.h"

#define MOD_TV     0
#define MOD_REPLAY 1
#define MOD_OSD    2

#define REPLAY_PLAY  "> "
#define REPLAY_PAUSE "||"
#define REPLAY_FF    ">>"
#define REPLAY_FB    "<<"

extern int SleepTime;
extern int MsgTime;
extern char ProgChar;

//// SQ: Kanal-Nummer merken fuer Aktualisierung des Programmes

///////////////////////////////////////////////////
// activy300 LCD plugin for VDR
//
// Original written by:
//
// Meinrad Sauter <meinrad.sauter@gmx.de>
// andreas 'randy' weinberger <vdr@smue.org>
//
// rearranged, beautified and rewritten by:
//
// Markus Geisler <geisler@primusnetz.de>
//
// optimized by:
// Sascha Quint <sascha.quint@gmx.net>

/////////////////////////////////////////////////////////
// Konstruktor


cLCD::cLCD()
{
   // waittimer for status messages
   stop = false;
   running = true;
   displayMode = MOD_TV;

   infoTimer = 0;
   szInfoLine[0] = szInfoLine[1] = NULL;

   szProgName[0]='\0';
   szRecName[0]='\0';
   szRecStatus[0]='\0';
   szReplayName[0]='\0';
   szOsdTitle[0]='\0';
   szOsdText[0]='\0';
   szMsgText[0]='\0';
   szVolText[0]='\0';
   PlayControl = NULL;
   currentChannel = 0;

   // new instance of LCD
   myLCD = new activyLCD();
}

/////////////////////////////////////////////////////////
// Destruktor
cLCD::~cLCD()
{
   stop = true;
   while( running ) usleep(10000);
   delete myLCD;
}

/////////////////////////////////////////////////////////
// Set Channel Name
void cLCD::ChannelSwitch(const cDevice *device, int chNum)
{
#ifdef DEBUG
   isyslog("ChannelSwitch called with Device %d and ChanNr %d",device->IsPrimaryDevice(),chNum);
#endif
   const char * szChN;

   cMutexLock lock(&_mutex);
   if( chNum && device->IsPrimaryDevice() && chNum == cDevice::CurrentChannel() && currentChannel != chNum )
   {
      szChN = Channels.GetByNumber(chNum)->Name();
      currentChannel = chNum;
      // only proceed, if valid data
      if( szChN != NULL && strlen( szChN ) > 0 )
      {
         // set channel name
         myLCD->FormatString( szChannelName, szChN, sizeof( szChannelName ) );
      }
      else
      {
         memset( szChannelName, ' ', sizeof( szChannelName ) );
         szChannelName[ sizeof( szChannelName ) - 1 ] = '\0';
      }
      memset( szProgName, ' ', sizeof( szProgName ) );
      szProgName[ sizeof( szProgName ) - 1 ] = '\0';
		_doUpdate.Broadcast();
   }
#ifdef DEBUG
   isyslog("Out ChannelSwitch");
#endif
}

/////////////////////////////////////////////////////////
// Set Program
void cLCD::OsdProgramme(time_t tPresentTime, const char *szPresentTitle, const char *szPresentSubtitle,
                        time_t tFollowingTime, const char *szFollowingTitle, const char *szFollowingSubtitle)
{
#ifdef DEBUG
   isyslog("In OsdProg <%s>", szPresentTitle);
#endif
   cMutexLock lock(&_mutex);

   // only proceed, if valid data
   if( szPresentTitle != NULL && strlen( szPresentTitle ) > 0 )
   {
      // set channel name
      myLCD->FormatString( szProgName, szPresentTitle, sizeof( szProgName ) );
   }
   else
   {
      memset( szProgName, ' ', sizeof( szProgName ) );
      szProgName[ sizeof( szProgName ) - 1 ] = '\0';
   }
   _doUpdate.Broadcast();
#ifdef DEBUG
   isyslog("Out OsdProg");
#endif
}


/////////////////////////////////////////////////////////
// OsdChannel

void cLCD::OsdChannel(const char *szText)
{
#ifdef DEBUG
   isyslog("In OsdChannel <%s>", szText );
#endif
   cMutexLock lock(&_mutex);
   if( szText )
   {
      displayMode = savedMode = MOD_TV;
      myLCD->FormatString( szMsgText, szText, sizeof( szMsgText ));
      szInfoLine[0] = szMsgText;
      szInfoLine[1] = NULL;
      SetInfoTimer();

      _doUpdate.Broadcast();
   }
#ifdef DEBUG
   isyslog("Out OsdChannel");
#endif
}



/////////////////////////////////////////////////////////
// Set Recording
void cLCD::Recording(const cDevice *device, const char *szName, const char *FileName, bool On)
{
#ifdef DEBUG
   isyslog("In Recording <%s>", szName);
#endif
   cMutexLock lock(&_mutex);

   // only proceed, if valid data
   if( szName )
   {
      RecordingInfo info;
      info.device = device;
      info.name = szName;
      _recordings.push_back(info);
      strncpy( szRecStatus,tr("Recording started:"), sizeof(szRecStatus) );

      myLCD->FormatString( szRecName, szName, sizeof( szRecName ));
   }
   else
   {
      Recordings::iterator it = _recordings.begin();
      for (; it != _recordings.end(); ++it) {
         if ((*it).device == device && StoppedTimer((*it).name.c_str())) {
            myLCD->FormatString( szRecName, (*it).name.c_str(), sizeof( szRecName ));
            _recordings.erase(it);
            break;
         }
      }
      strncpy( szRecStatus,tr("Recording stopped:"), sizeof(szRecStatus) );
   }
   szInfoLine[0] = szRecStatus;
   szInfoLine[1] = szRecName;
   SetInfoTimer();
   _doUpdate.Broadcast();
#ifdef DEBUG
   isyslog("Out Recording");
#endif
}


/////////////////////////////////////////////////////////
// Replay Control
void cLCD::Replaying(const cControl *control, const char *szName, const char *FileName, bool On)
{
#ifdef DEBUG
   isyslog("In replay <%s>", szName);
#endif
   cMutexLock lock(&_mutex);
   if( szName )
   {
      displayMode = savedMode = MOD_REPLAY;
      PlayControl = (cControl *)control;

      myLCD->FormatString( szReplayName, szName, sizeof( szReplayName ));
   }
   else
   {
      if( displayMode != MOD_OSD )
         displayMode = MOD_TV;

      savedMode = MOD_TV;
      PlayControl = NULL;
   }
   _doUpdate.Broadcast();
#ifdef DEBUG
   isyslog("Out replay");
#endif
}
/////////////////////////////////////////////////////////
// Menu Control

void cLCD::OsdCurrentItem(const char *szText)
{
#ifdef DEBUG
   isyslog("In OsdCurr <%s>", szText );
#endif
   cMutexLock lock(&_mutex);
   if( szText )
   {
      displayMode = MOD_OSD;

      myLCD->FormatString( szOsdText, szText, sizeof( szOsdText ));
   }
   _doUpdate.Broadcast();
#ifdef DEBUG
   isyslog("Out OsdCurr");
#endif
}


/////////////////////////////////////////////////////////
// Set Title

void cLCD::OsdTitle(const char *szText)
{
#ifdef DEBUG
   isyslog("In OsdTitle <%s>", szText );
#endif
   cMutexLock lock(&_mutex);
   if( szText )
   {
      displayMode = MOD_OSD;

      myLCD->FormatString( szOsdTitle, szText, sizeof( szOsdTitle ));
   }
   _doUpdate.Broadcast();
#ifdef DEBUG
   isyslog("Out OsdTitle");
#endif
}


/////////////////////////////////////////////////////////
// Status Information

void cLCD::OsdStatusMessage(const char *szText)
{
#ifdef DEBUG
   isyslog("In StatMsg <%s>", szText );
#endif
   cMutexLock lock(&_mutex);
   if( szText )
   {

      myLCD->FormatString( szMsgText, szText, sizeof( szMsgText ), true );

      szInfoLine[0] = szMsgText;
      if(strlen(szMsgText) > LCD_COLUMNS)
      {
         szInfoLine[1] = szMsgText + LCD_COLUMNS;
         szInfoLine[0][ LCD_COLUMNS - 1 ] = '\0';
      }
      else
         szInfoLine[1] = NULL;

      SetInfoTimer();
   }
   _doUpdate.Broadcast();
#ifdef DEBUG
   isyslog("Out StatMsg");
#endif
}


/////////////////////////////////////////////////////////
// Menu Control

void cLCD::OsdClear()
{
#ifdef DEBUG
   isyslog("In OsdClear");
#endif
   cMutexLock lock(&_mutex);
   displayMode = savedMode;
   _doUpdate.Broadcast();
#ifdef DEBUG
   isyslog("Out OsdClear");
#endif
}


/////////////////////////////////////////////////////////
// Set Volume

void cLCD::SetVolume(int volume, bool muted)
{
#ifdef DEBUG
   isyslog("In SetVol");
#endif
   cMutexLock lock(&_mutex);
   int vol = ( volume * 18 ) / 255;

   szVolText[0] = '[';
   if( muted )
   {
      strcpy(szVolText + 1,"    -- MUTE --    ");
   }
   else
   {
      memset( szVolText + 1, '#', vol );
      memset( szVolText + 1 + vol, ' ', 18 - vol );
   }
   strcpy(szVolText + 19,"]");
   szInfoLine[1] = szVolText;
   szInfoLine[0] = (char *)tr( "Volume" );

   SetInfoTimer();
   _doUpdate.Broadcast();
#ifdef DEBUG
   isyslog("Out SetVol");
#endif
}

/////////////////////////////////////////////////////////
// SQ: get Programm
//     aktuelle Programminformation ides laufenden Kanals vom VDR lesen
void cLCD::GetProgram()
{
   // read current Programme from VDR
   const cEvent * present = NULL;
   cSchedulesLock schedulesLock;
   const cSchedules * schedules = cSchedules::Schedules(schedulesLock);
   bool bFound = false;

   if( schedules && currentChannel )
   {
      cChannel *ChID = Channels.GetByNumber(currentChannel);
      if( ChID )
      {
         const cSchedule * schedule = schedules->GetSchedule(ChID->GetChannelID());
         if( schedule )
         {
            if( (present = schedule->GetPresentEvent()) != NULL )
            {
               // set channel name
               myLCD->FormatString( szProgName, present->Title(), sizeof( szProgName ) );
               bFound = true;
            }
         }
      }	 
   }
   if(bFound == false)
   {
      memset( szProgName, ' ', sizeof( szProgName ) );
      szProgName[ sizeof( szProgName ) - 1 ] = '\0';
   }
}


/////////////////////////////////////////////////////////
// Thread Action

void cLCD::Action(void)
{
   static int updCounter = 0;
   struct timeval now;
   running=true;
   char szBufLine[256];
   char * szLine[LCD_ROWS] = { NULL, NULL };


   usleep(1000000);
	_mutex.Lock(); // mutex gets ONLY unlocked when sleeping
   while( !stop )
   {
#ifdef DEBUG
      isyslog("Action Thread");
#endif
      gettimeofday(&now,NULL);

      switch( displayMode )
      {
         //
         // case 0 : Channeldisplay
         //
         case MOD_TV:
         {
            struct tm uhr;
            time_t val;
            val = time(NULL);
            localtime_r(&val,&uhr);

            snprintf(szBufLine,sizeof(szBufLine),"%-14.14s %02d:%02d", szChannelName, uhr.tm_hour, uhr.tm_min);
            szLine[0] = szBufLine;

            // SQ: ab und zu mal die Programminformation nachlesen
            //     die Sendung koennte sich ja geaendert haben
            if( ++updCounter > 20 )
            {
               GetProgram();
               updCounter = 0;
            }
            szLine[1] = szProgName;
            break;
         }
         case MOD_REPLAY:
         {
            char * szPlaySymbol = REPLAY_PLAY;
            char szProgress[8];
            int Current = 0, Total = 0, Speed = 0;
            bool Play, Forward;

            szProgress[0] = '\0';

            if( PlayControl && PlayControl->GetReplayMode(Play,Forward,Speed) )
            {
               if( Speed == -1 )
               {
                  if( Play )
                     szPlaySymbol = REPLAY_PLAY;
                  else
                     szPlaySymbol = REPLAY_PAUSE;
               }
               else
               {
                  if( Forward )
                     szPlaySymbol = REPLAY_FF;
                  else
                     szPlaySymbol = REPLAY_FB;
               }
            }

            if( PlayControl && PlayControl->GetIndex(Current,Total) )
            {
               unsigned int pp;
	
               if( Total >= 1 && Current <= Total && Current >= 0 )
               {
                  pp = ( 7*Current ) / Total;
                  if( pp >= sizeof(szProgress) )
                     pp = sizeof(szProgress) - 1;
               }
               else
	               pp = 0;


               memset( szProgress, ProgChar, pp );
               szProgress[ pp ] = '\0';

               snprintf(szBufLine,sizeof(szBufLine),"%-2.2s|%-7.7s|%-9.9s",szPlaySymbol,szProgress,(const char *)IndexToHMSF(Current,false));
               szLine[0] = szBufLine;
            }
            szLine[1] = szReplayName;

            break;
         }
         case MOD_OSD:
         {
            szLine[0] = szOsdTitle;
            szLine[1] = szOsdText;
            break;
         }
         default:
            szLine[0] = szLine[1] = NULL;
            break;

      }

      myLCD->SetInfo( cRecordControls::Active(), infoTimer > 0 ? true : false );

      if( infoTimer > 0 )
      {
         myLCD->SendText( szInfoLine );
         infoTimer--;
      }
      else
         myLCD->SendText( szLine );

//      myLCD->CheckErrors();

      // wait for event or timeout
      _doUpdate.TimedWait(_mutex, SleepTime * 100);
   }
   running=false;
}


bool cLCD::StoppedTimer(const char *szName)
{
	cTimer *timer = Timers.First();
	while (timer) {
		if (strcmp(szName, timer->File()) == 0)
			break;
		timer = Timers.Next(timer);
	}
	return timer == NULL || !timer->Recording();
}


void cLCD::SetInfoTimer(void)
{
   infoTimer = ( MsgTime * 10 ) / SleepTime;
}
#endif
