#ifndef __ACTIVYLCD_CPP
#define __ACTIVYLCD_CPP

#include<sys/wait.h>
#include "activylcd.h"
#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <time.h>
#include <stdlib.h>
#include <ctype.h>
#include <vdr/tools.h>

// sonstige Variablen
extern unsigned int ScrollSpeed;
extern bool Umlauts;              // Show Umlauts
extern int Scrolling;             // Kind of scrolling
extern int LEDRecord;             // Message LED
extern int LCDBrightness;         // Brightness 0 == bright, 4 == dark...
extern int LCDBrightnessOnExit;
extern bool LCDavailable;
//
//// SQ: fuer Semaphor
#define KEY 4711 // Einduetigen Key

extern char *resetScript;

#undef LCDDEBUG
#undef D_EVENTFILTER

///////////////////////////////////////////////////
// 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

activyLCD::activyLCD()
{
   char * szLine[LCD_ROWS] = { " ", " " };

#ifdef DEBUG
   isyslog("activyLCD");
#endif

   actBrightness = -1;
   bRecStat = bInfoStat = true;
   
   if( resetScript != NULL )
      system( resetScript );

   SerialPortOpen();
   SetPowerButton(1);  // always enable PowerButton
   SetLCDBrightness(LCDBrightness);

   SetInfo(false,false);

   SendCommand(HWREV);
   SendCommand(FWREV);
   SendCommand(LCD_CLEAR);
   
   picError = false;
   SendText( szLine ); // for checking if Display is available
   if( picError )
   {
      isyslog("activyLCD: Set Text failed - assuming no LCD available");
      LCDavailable = false;
      picError = false;
   }

   actLine[0][0] = actLine[1][0] = '\x00';
   actPos[0] = actPos[1] = 0;
#ifdef DEBUG
   isyslog("activyLCD: Constructor done");
#endif
}

/////////////////////////////////////////////////////////
// Destruktor

activyLCD::~activyLCD()
{
#ifdef DEBUG
   isyslog("~activyLCD");
#endif
   SendCommand(LCD_CLEAR);
   SetPowerButton(1);
   SetLCDBrightness(LCDBrightnessOnExit);
   SetInfo(false,false);
   SerialPortClose();
#ifdef LCDDEBUG
   fclose(logfile);
#endif
#ifdef DEBUG
   isyslog("acticyLCD: Destructor done");
#endif
}

/////////////////////////////////////////////////////////
// Open Serial Port

int activyLCD::SerialPortOpen(void)
{
   struct termios options;

   // open serial device
   fd = open(LCD_PORT, O_RDWR | O_NOCTTY | O_NDELAY);

   // check result
   if( fd == -1 )
   {
      // error - port could not be opened
      isyslog("alcd-plugin: SerPort could not be opened");
      return(-1);
   }
   else
   {
      // ok - port opened
      fcntl(fd, F_SETFL, 0);
      // Get the current options for the port...
      tcgetattr(fd, &options);
      // Set the baud rates to 38400...
      cfsetispeed(&options, B38400);
      cfsetospeed(&options, B38400);
      // Enable the receiver and set local mode...
      options.c_cflag |= (CLOCAL);
      options.c_cflag &= ~PARENB;
      options.c_cflag &= ~CSTOPB;
      options.c_cflag &= ~CSIZE;
      options.c_cflag |= CS8;
      options.c_oflag &= ~OPOST;
      options.c_oflag &= ~(ICANON | ECHO | ECHOE | ISIG);
      options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
      options.c_cc[VMIN]  = 0;                  /* warten auf min. 0 Zeichen */
      options.c_cc[VTIME] = 10;                 /* Timeout in deciseconds */

      // Set the new options for the port...
      tcsetattr(fd, TCSANOW, &options);
      // flush ser port
      tcflush(fd, TCIOFLUSH);
       
#ifdef DEBUG
      isyslog("alcd-plugin: SerPort opened");
#endif
      return(fd);
   }
}

/////////////////////////////////////////////////////////
// Close Serial Port

void activyLCD::SerialPortClose()
{
   close(fd);

#ifdef DEBUG
   isyslog("alcd-plugin: SerPort closed");
#endif
}

/////////////////////////////////////////////////////////
// Send Command
void activyLCD::SendCommand(char *command)
{
   if( !LCDavailable &&
       ( !strncmp( command, MESSAGELED_ON, 2 )   ||
         !strncmp( command, MESSAGELED_OFF, 2 )  ||
         !strncmp( command, LCD_WRITE_LINE0, 1 ) ||
         !strncmp( command, LCD_CLEAR, 2 )       ||
         !strncmp( command, LCD_DARKEST, 1 ) ) )
   {
      return;
   }      
   
   PicWrite( command, 2 );
}

/////////////////////////////////////////////////////////
// Send Command
void activyLCD::SendText(char *text[LCD_ROWS])
{
   char buffer[ LCD_COLUMNS + 3 ];
   bool write = false;
   char *txt;

   if( !LCDavailable )
      return;

   for( int i=0; i < LCD_ROWS; i++)
   {
      if(text[i] == NULL)
         txt = "";
      else
         txt = text[i];


      if( strcmp( actLine[i], txt ) )
      {
         strncpy( actLine[i], txt, sizeof( actLine[i] ) );
         scroll[i] = actPos[i] = 0;
         write = true;
      }
      else
      {
         if( strlen( actLine[i] ) > LCD_COLUMNS && ScrollSpeed > 0 )
         {
            if( ++(scroll[i]) > ScrollSpeed )
            {
               scroll[i] = 0;
               write = true;
               if( actPos[i] + LCD_COLUMNS >= strlen( actLine[i] ) )
                  actPos[i] = 0;
               else
               {
                  if( Scrolling == SCROLL_BLOCK )
                     actPos[i] += LCD_COLUMNS;
                  else
                     actPos[i]++;
               }
            }
         }
      }
      if( write )
      {
         snprintf( buffer, sizeof(buffer), "%s%-20.20s%c", i==0 ? LCD_WRITE_LINE0 : LCD_WRITE_LINE1, actLine[i] + actPos[i], '\x00');
         PicWrite( buffer, LCD_COLUMNS + 3 );
      }
   }
   SetLCDBrightness(LCDBrightness);
}


/////////////////////////////////////////////////////////
// Send Command to LCD-Device
void activyLCD::PicWrite(char *command,int length)
{
   char picRes[6];
   int result;

   if( !command || !length )
      return;

#ifdef D_EVENTFILTER
   write(fd,EVENTFILTER,2);
   usleep(10000);
   read(fd,picRes, 2);

   // eventfilter set?
   CheckPicRes(picRes);
#endif

#ifdef DEBUG
   isyslog("alcd-plugin: Send to lcd: <%02x%02x>",(unsigned char)command[0], (unsigned char)command[1]);
   if( (unsigned)command[0] == (unsigned)LCD_WRITE_LINE0[0] )
      isyslog("alcd-plugin: Text <%s> - %d",(char *)(command + 2), length);
#endif

   // send real command to lcd
   write(fd,command,length);
   usleep(10000);
   result = read(fd,picRes,2);

   while( (result == 2) && (picRes[0] != command[0]) )
   {
      isyslog("alcd-plugin: received: %d <%02x%02x>",result,(unsigned char)picRes[0],(unsigned char)picRes[1]);
      if( picRes[1] == command[0] )
      {
         picRes[0] = command[0];
         result = read(fd,picRes+1,1) + 1;
      }
      else
         result = read(fd,picRes,2);
   }
#ifdef DEBUG
   isyslog("alcd-plugin: result: %02x%02x (%d)",(unsigned char)picRes[0],(unsigned char)picRes[1], result);
#endif
   if( (result == 2) && (picRes[0] == command[0]) && (picRes[1] & PIC_RES_ACK) )
   {
      // ergebniswerte auswerten
      switch( command[0] )
      {
         // hw revision
         case 0x89 :
            // hw revision - 1 byte!
            read(fd,picRes+2,1);
            isyslog("alcd-plugin: Activy300-LCD-HW Revision: %i",picRes[2]);
            
            break;
         // fw revision
         case 0x8a :
            // fw revision - 4 bytes!
            read(fd,picRes+2,4);
            isyslog("alcd-plugin: Activy300-LCD-SW Revision: Release: %i Version: %i PreRelease: %c VirtualNumber: %i",
                    picRes[2],picRes[3],picRes[4],picRes[5] );
   
            break;
         // blast ir buffer
         case 0x85 :
         // set LED
         case 0x90 :
         // set powerbutton
         case 0x94 :
         // clear lcd
         case 0x9b :
         // writeline
         case 0x9a :
         // set brightness
         case 0xf0 :
         // set brightness
            break;
            // buttons
         case 0x92 :
            // read buttons
   
            // bit 0 reserved
            // bit 1 power button
            // bit 2 up button
            // bit 3 down button
            // bit 4 left button
            // bit 5 right button
            // bit 6 select button
            // bit 7 reserved
   
   
            read(fd,picRes+2,1);
            picRes[2] &= ~0x81;
            if( picRes[2] != 0x00 )
              isyslog("alcd-plugin: %02x button pressed", picRes[2]);
   
            break;
         default :
            // command unknown!
            isyslog("alcd-plugin: ERROR: unknown command! <%02x%02x>",(unsigned char)command[0], (unsigned char)command[1]);
            break;
      }
   }
   else
   {
      picError = true;
      isyslog("alcd-plugin: Failing Command: <%02x%02x>",(unsigned char)command[0], (unsigned char)command[1]);
      isyslog("alcd-plugin: ResCode: %d <%02x%02x>",result, (unsigned char)picRes[0],(unsigned char)picRes[1]);
      if(command[0] == LCD_WRITE_LINE0[0])
         isyslog("Failing Text <%s> - %d",(char *)command + 2, length);
   }

#ifdef D_EVENTFILTER
   write(fd,EVENTFILTEROFF,2);
   usleep(10000);
   read(fd,picRes, 2);

   // eventfilter set?
   CheckPicRes(picRes);
#endif

#ifdef DEBUG
   isyslog("alcd-plugin: SendCommand done");
#endif
}

/////////////////////////////////////////////////////////
// Check Errors
bool activyLCD::CheckErrors()
{
   bool bRc = false;

   if( picError == true )
   {
      Reset();
      bRc = true;
   }
   return bRc;
}


void activyLCD::Reset()
{
   unsigned char picRes[6];
   int result;

   isyslog("alcd-plugin: ERROR: Performing PICRESET!");
   usleep(100000);
   // Clear read buffer
   result = read(fd,picRes,6);
   isyslog("alcd-plugin: Result Clear: %02x%02x%02x%02x%02x%02x (%d)",picRes[0],picRes[1],picRes[2],picRes[3],picRes[4],picRes[5], result);

   write(fd,PICRESET,2);
   usleep(10000);
   result = read(fd,picRes,2);
   isyslog("alcd-plugin: Result PICRESET: %02x%02x (%d)",picRes[0],picRes[1], result);

   if( resetScript != NULL )
   {
      int rc;
      SerialPortClose();
      rc = system( resetScript );
      isyslog("alcd-plugin: Executing <%s> result: %x", resetScript, rc );
      SerialPortOpen();
   }

   LCDavailable = true;
   picError = false;
   actBrightness = -1;
   bRecStat = bInfoStat = false;

   SetPowerButton(0);  // always enable PowerButton
   SetLCDBrightness(LCDBrightness);
   SetInfo(true,true);

   SendCommand(LCD_CLEAR);
   actLine[0][0] = actLine[1][0] = '\x00';
   actPos[0] = actPos[1] = 0;
}

/////////////////////////////////////////////////////////
// Set Power Button

void activyLCD::SetPowerButton(bool status) // switch powerbutton on
{
   switch( status )
   {
      case 0:
         SendCommand(POWERBUTTON_SCANCODE_OFF);
         SendCommand(POWERBUTTON_OFF);
         break;

      case 1:
         SendCommand(POWERBUTTON_SCANCODE_ON);
         SendCommand(POWERBUTTON_ON);
         break;
   }
}

/////////////////////////////////////////////////////////
// Set LCD Brightness

void activyLCD::SetLCDBrightness(const int brightness)
{
   if( brightness != actBrightness )
   {
      actBrightness = brightness;
      switch( brightness )
      {
         case BRIGHTEST: // superbright
            SendCommand(LCD_BRIGHTEST);
            break;

         case BRIGHT: // bright
            SendCommand(LCD_BRIGHT);
            break;

         case MEDIUM: // semibright
            SendCommand(LCD_MEDIUM);
            break;

         case DARK: // dark
            SendCommand(LCD_DARK);
            break;

         case DARKEST: // off
            SendCommand(LCD_DARKEST);
            break;
      }
   }
}

/////////////////////////////////////////////////////////
// Set RecordLED

void activyLCD::SetInfo(bool bRec, bool bInfo)
{
   char *cmd = NULL;

   if( bRecStat != bRec )
   {
      bRecStat = bRec;
      if( bRecStat == true )
      {
         if( LEDRecord == LEDREC_RED )
            cmd = MESSAGELED_ON;
         else
            cmd = BASICLED_ON;
      }
      else
      {
         if( LEDRecord == LEDREC_RED )
            cmd = MESSAGELED_OFF;
         else
            cmd = BASICLED_OFF;
      }

      SendCommand(cmd);
   }
   if( bInfoStat != bInfo )
   {
      bInfoStat = bInfo;
      if( bInfoStat == true )
      {
         if( LEDRecord == LEDREC_RED )
            cmd = BASICLED_ON;
         else
            cmd = MESSAGELED_ON;
      }
      else
      {
         if( LEDRecord == LEDREC_RED )
            cmd = BASICLED_OFF;
         else
            cmd = MESSAGELED_OFF;
      }

      SendCommand(cmd);
   }
}

/////////////////////////////////////////////////////////
// Format String according to settings

void activyLCD::FormatString( char *output, const char *input, int outLen, bool split )
{
   output[ 0 ] = '\0';
   // only proceed, if valid data
   if( input )
   {
      char *lsp = output;
      char *lbp = output;
      char *in = (char *)input;
      while( *in == ' ' )
      {
         in++;
      }
      char *out = output;
      char *endPtr = output + outLen - 2*LCD_COLUMNS;
      char *nextSplitPos = output + LCD_COLUMNS;

      while( out < endPtr && *in != '\0' )
      {
         if( ( Scrolling == SCROLL_BLOCK || split == true ) && out >= nextSplitPos )
         {
            if( *in == ' ' && out == nextSplitPos )
            {
               while( *in == ' ' )
               {
                  in++;
               }
            }
            else
            {
               char *endPos = out;
               char *actPos;
               out = nextSplitPos;

               if( nextSplitPos - lbp < ( LCD_COLUMNS / 2 ) )
                  actPos = lbp + 1;
               else if( nextSplitPos - lsp < ( LCD_COLUMNS / 2 ) )
                  actPos = lsp + 1;
               else if( nextSplitPos - lbp < ( LCD_COLUMNS * 3 / 4 ) )
                  actPos = lbp + 1;
               else if( nextSplitPos - lsp < ( LCD_COLUMNS * 3 / 4 ) )
                  actPos = lsp + 1;
               else
                  actPos = endPos;

//               isyslog("alcd-plugin: FS <%-10.10s><%-10.10s><%-10.10s>", actPos, endPos, out);

               if( actPos < endPos )
               {
	               char szTmp[LCD_COLUMNS];
                  int sLen = endPos - actPos;

		            strncpy( szTmp, actPos, sLen );
		            szTmp[sLen] = '\0';
                  memset( actPos, ' ', sLen );
                  strcpy( nextSplitPos, szTmp );
                  out = nextSplitPos + sLen;
               }
//              isyslog("alcd-plugin: FS <%-10.10s><%-10.10s>", lbp,actPos);
	
            }
            if( Scrolling == SCROLL_BLOCK )
               nextSplitPos += LCD_COLUMNS;
         }

         if( Umlauts == false )
         {
            switch( *in )
            {
               case '\x09':  // Tab
                  *out = ' ';
                  break;

               case 'ä':    // ae
                  *out = 'a';
                  out++;
                  *out = 'e';
                  break;

               case 'ö':    // oe
                  *out = 'o';
                  out++;
                  *out = 'e';
                  break;

               case 'ü':    // ue
                  *out = 'u';
                  out++;
                  *out = 'e';
                  break;

               case 'ß':    // ss
                  *out = 's';
                  out++;
                  *out = 's';
                  break;

               case 'Ä':    // AE
                  *out = 'A';
                  out++;
                  if( islower( *(in+1) ) )
                     *out = 'e';
                  else
                     *out = 'E';
                  break;

               case 'Ö':    // OE
                  *out = 'O';
                  out++;
                  if( islower( *(in+1) ) )
                     *out = 'e';
                  else
                     *out = 'E';
                  break;

               case 'Ü':    // UE
                  *out = 'U';
                  out++;
                  if( islower( *(in+1) ) )
                     *out = 'e';
                  else
                     *out = 'E';
                  break;

               default:
                  *out = *in;
                  break;
            }
         }
         else
         {
            switch( *in )
            {
               case '\x09':  // Tab
                  *out = ' ';
                  break;

               case 'ä':    // ae
               case 'Ä':    // AE
                  *out = '\xE1';
                  break;

               case 'ö':    // oe
               case 'Ö':    // OE
                  *out = '\xEF';
                  break;

               case 'ü':    // ue
               case 'Ü':    // UE
                  *out = '\xF5';
                  break;

               case 'ß':    // ss
                  *out = '\xE2';
                  break;

               default:
                  *out = *in;
                  break;
            }
         }
         if( *in == ' ' )
         {
            lbp = out;
            while( *(in+1) == ' ' )
            {
               in++;
            }
         }
         else
         {
            if( !isalnum( *out ) )
            {
               lsp = out;
            }
         }
         out++;
         in++;
      }
      while( out > output && *(out-1) == ' ' )
      {
         out--;
      }
      if( (out > output + LCD_COLUMNS) && (Scrolling != SCROLL_BLOCK) )
         sprintf( out, "    %20.20s%c", output, '\x00');
      else
         *out = '\0';
   }
}

#endif
