#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 <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <math.h>
#include <time.h>

#include "multilcd.h"
#include <vdr/tools.h>
#include <vdr/config.h>
// #define M_DEBUG


mLCD::mLCD()
{
  int n;
  stop = false;
  starttest = false;
  title=false;
  lcdmodus = 0;
  OldProgressValue = 0;
  for (n=0;n<4;n++) cardstate[n] = 0;
  memset(line1,' ',10);
  memset(oldline1,' ',10);
  memset(line2,' ',10);
  memset(oldline2,' ',10);
  for (n=0;n<25;n++)
    {
      symbol[n] = 0;
      oldsymbol[n] = 0;
    }
  n = read_font();
  n = open_port();
  setup_port();
  n = init_display();  
}

mLCD::~mLCD()
{
  int i;
  stop = true;
  while (running) usleep(100000);
  clear_text();
  for (i=0;i<25;i++) set_symbol(i,0);
  set_symbol(9,1);
  close_port();
}

int mLCD::open_port(void)
{
  fd = open(LCD_PORT, O_RDWR | O_NOCTTY | O_NDELAY);
  if (fd == -1)
    {
      isyslog("open_port: Unable to open %s", LCD_PORT);
    }
  else
    fcntl(fd, F_SETFL, 0);
  return (fd);
}

void mLCD::close_port()
{
  close(fd);
}

int mLCD::init_display()
{
  unsigned char initseq;
  int n;
  memset(&initseq,INIT,1);
  n = write(fd, &initseq, 1);
  usleep(10000);
  return n;
}

int mLCD::set_char(int line, int pixel, unsigned int bitmuster[6])
{
  unsigned char seq[9];
   int n;
   int i;
   seq[0] = TEXT;
   seq[1] = line;
   seq[2] = pixel;
   for(i=3; i<9; i++) seq[i] = bitmuster[i-3];
   n = write(fd,&seq,9);
   n = write(fd,&seq,9);
   return n;
}



/*
Modus 0 = aus, 1 = ein, 2 = blinkend
 */
int mLCD::set_symbol(int symbol, int mode)
{
  unsigned char sequence[3];
  int n;
  sequence[0] = SYM;
  sequence[1] = symbol;
  sequence[2] = mode;
  n = write(fd,&sequence,3);
  n = write(fd,&sequence,3);
  return n;  
}

void mLCD::setup_port()
{
  struct termios options;
  /*
   * Get the current options for the port...
   */
  tcgetattr(fd, &options);
  /*
   * Set the baud rates to 9600...
   */
  cfsetispeed(&options, B9600);
  cfsetospeed(&options, B9600);
  /*
   * 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;
  // Not needed, thanks to steini
  //  options.c_oflag &= ~OPOST; 
  /*
   * Set the new options for the port...
   */
  tcsetattr(fd, TCSANOW, &options);
}

void mLCD::test_display()
{
  snprintf(line1,11,"%s",VDRVERSION);
  snprintf(line2,11,"%s",MULTIVERSION);
  print_line(0,line1);
  print_line(1,line2);
  usleep(2000000);
  int i;
  for (i=0;i<12;i++)
    {
      oldline1[i] = line1[i];
      oldline2[i] = line2[i];
    }
}

int mLCD::read_font()
{
  //Initialisierung des Arrays
  int i;
  int j;
#ifdef M_DEBUG
  dsyslog(LOG_INFO,"Reading Font description from %s",FONTFILE);
#endif
  for (i=0;i<256;i++)
    {
      for(j=0;j<6;j++) font[i][j] = 0;
    }
  //Oeffnen der Fontdatei
  const char *name;
  FILE *f=fopen(name=FONTFILE,"r");
 if(f){
   int n, i;
   char buffer[128];
   char erg[128];
   unsigned int code;
   unsigned int res[6];
   code = 0;
   while(fgets(buffer,sizeof(buffer),f)){

     n = sscanf(buffer,"%x %x %x %x %x %x %s",&res[0],&res[1],&res[2],&res[3],&res[4],&res[5],&erg[0]); 
     code = erg[0];
     if (strstr(erg,"space")!=NULL) code = ' ';
     for(i=0;i<6;i++) font[code][i] = res[i];
   }
 fclose(f);
 }

 return 0;
}

void mLCD::print_line(int line,  char text[11])
{
 unsigned int bitmuster[6];
  int i, j;
  if(strlen(text)<10)
    {
      for(i=strlen(text);i<10;i++) text[i] = ' ';
      text[10] = '\0';
    }
 for(i=0;i<10;i++)
   {
     //Bitmuster aus Fontdefinition kopieren
     for(j=0;j<6;j++)
       {
	 bitmuster[j] = font[(int) text[i]][j];
       }
     //Zeichen in Display nur schreiben, falls veraendert 
     switch (line){ 
     case 0:
       if ((line1[i]!=oldline1[i]) || refresh) set_char(line, i*6, bitmuster);
       break;
     case 1:
       if ((line2[i]!=oldline2[i]) || refresh) set_char(line, i*6, bitmuster);
       break;
     }
   }
}

void mLCD::progressbar(int percent)
{
  long int numberoflines;
  int i,j,k;
  unsigned int bitmuster[10][6], bit_send[6];
  numberoflines = (long int) ((60*percent)/100);
  for(i=0;i<10;i++)
    {
      for(j=0;j<6;j++)
	{
	  if((i*6+j)<numberoflines) bitmuster[i][j] = 0xff;
	  if((i*6+j)>=numberoflines) bitmuster[i][j] = 0x00;
	}
      for(k=0;k<6;k++) bit_send[k] = bitmuster[i][k];
      if (bitmuster[i] != oldmuster[i])
	set_char(1,i*6,bit_send);
    }
}

void mLCD::display_volume(int timeout, int volume, bool muted)
{
  int percent;
  int n,m;
  if (volume==0)
    {
      symbol[4] = 2;
    }
  else
    {
      symbol[4] = 0;
      for (n=0;n<10;n++)
	{
	  for(m=0;m<6;m++) oldmuster[n][m] = 0x00;
	}
      if (lcdmodus != 1) oldmodus = lcdmodus;
      lcdmodus = 1;
      percent =  lround((volume*100)/255);
      ProgressValue = percent;
      DisplaySeconds = timeout;
      gettimeofday(&VolumeStartTime,NULL);
    }
}

void mLCD::clear_text() // Don't use in running thread, instead assign line1 and line2 !!!
{
  refresh = true;
  print_line(0,"          ");
  print_line(1,"          ");
  refresh = false;
}

void mLCD::SetProgress(const char *HMSF, int percent)
{
  ProgressValue = percent;
  snprintf(line1,11,"%10s",HMSF);
}

void mLCD::SetChannelName (const char *Name)
{
  snprintf(chline2,11,"%s",Name);
}

void mLCD::SetChannelInfo (const char *Info)
{
  snprintf(chline2,11,"%s",Info);
}

void mLCD::SetChannel (int  ChannelNumber)
{
  snprintf(chline1,5,"%-4d",ChannelNumber);
}

void mLCD::SetRecordingState(int card, const char *Name)
{
  if (Name != NULL) cardstate[card]++;
  else cardstate[card]--;
}

void mLCD::ReplayControl(cControl *Control, const char *Name)
{
  if (Name == NULL)
    {
      ReplaySymbols(0);
      symbol[10] = 0;
      lcdmodus = 0;
      refresh = true;
    }
  else
    {
      ReplaySymbols(1);
      if ((strcmp(Name,"VCD") == 0) || (strcmp(Name,"DVD")== 0)) symbol[10] = 1;
      lcdmodus = 2;
      PlayControl = Control;
    }
}

void mLCD::ReplaySymbols(int mode)
{
  /* Description of Modes                          */
  /* 0 = Off; 1 = Play; 2 = Paused; 3 = FF; 4 = FB */
  int i;

  if (mode==0) lcdmodus = 1;
  else lcdmodus = 2;

  switch (mode) {
    
  case 0:
    for (i=12;i<22;i++) symbol[i] = 0;
    break;
  case 1:
    for (i=12;i<22;i++) symbol[i] = 0;
    symbol[14] = 1;
    break;
  case 2:
    for (i=12;i<22;i++) symbol[i] = 0;
    symbol[12] = 1;
    symbol[13] = 1;
    break;
  case 3:
    for (i=12;i<22;i++) symbol[i] = 0;
    symbol[14] = 1;
    symbol[15] = 1;
    break;
  case 4:
    for (i=12;i<22;i++) symbol[i] = 0;
    symbol[19] = 1;
    symbol[20] = 1;
    break;
  }
}
void mLCD::Action(void)
{
  bool redraw = true;
  running=true;
  while(!stop){
    gettimeofday(&now,NULL);
    if (starttest) 
      {
	refresh=true;
	test_display();
	refresh=false;
	starttest = false;
      }
    else
      {
    /* Recording Indicator */
    if ((cardstate[0] == 0) && (cardstate[1] == 0) && (cardstate[2] == 0) && (cardstate[3] == 0)){
      symbol[0] = 1;
    }
    else {
      symbol[0] = 2;
    }
    /* Generic Symbol Changes */
    if (symbol!=oldsymbol)
      {
	int i;
	for(i=0;i<25;i++)
	  {
	    if (symbol[i]!=oldsymbol[i])
	      {
		set_symbol(i,symbol[i]);
	      }
	  }
	for (i=0;i<25;i++)
	  {
	    oldsymbol[i] = symbol[i];
	  }
      }
 
    switch (lcdmodus) { 

      case 0:   /* Implementation of Channeldisplay */
	struct tm uhr;
	time_t val;
	int i;
	val = time(NULL);
	localtime_r(&val,&uhr);
	snprintf(line1,11,"%s %02d:%02d",chline1,uhr.tm_hour,uhr.tm_min); 
	for (i=0;i<12;i++)
	  {
	    line2[i] = chline2[i];
	  }

	if (strcmp(line1,oldline1)!=0 || refresh)
	  {
	    print_line(0,line1);
	    for (i=0;i<12;i++)
	      {
		oldline1[i] = line1[i];
	      }	   
	  }
	if (strcmp(line2,oldline2)!=0 || refresh)
	  {
	    print_line(1,line2);
	    for (i=0;i<12;i++)
	      {
		oldline2[i] = line2[i];
	      }
	  }
	refresh = false;
	break;
    case 1:   /* Implementation of Volumegraph */
      if (((now.tv_sec - VolumeStartTime.tv_sec)*1000000+now.tv_usec-VolumeStartTime.tv_usec ) > 1000000*DisplaySeconds  ) {
	 lcdmodus=oldmodus;
	 refresh=true;
	 title=false;
	 redraw=true;
      } else {
	int volumedelta;
	volumedelta = abs(ProgressValue-OldProgressValue);
	if(volumedelta >=5 || redraw)
	  {
	    if(!title) 
	      {
		sprintf(line1,"  Volume  ");
		print_line(0,line1);
		title=true;
		for (i=0;i<12;i++)
		  {
		    oldline1[i] = line1[i];
		  }
	      }
	    progressbar(ProgressValue);
	    OldProgressValue=ProgressValue;
	    redraw = false;
	  }
      }     
       break;
    case 2: /* Implementation of Replay */
      /* Current Playtime line 1 */
      int Current, Total, Speed;
      bool Play, Forward;
      /* Set the Replay Mode Symbols */
      if (PlayControl->GetReplayMode(Play,Forward,Speed))
	{
	  if (Play && Speed == -1) ReplaySymbols(1); // Normal Replay
	  if (!Play && Speed == -1) ReplaySymbols(2); // Pause
	  if (Forward && Speed != -1) ReplaySymbols(3); // Fast Forward
	  if (!Forward && Speed != -1) ReplaySymbols(4); // Fast Backward
	}
     
      /* Set up the Replay Progress */
      if (PlayControl->GetIndex(Current,Total))
	{
	  if (Total > 0 && Current <= Total)
	    {
	      ProgressValue = lround((Current*100)/Total);
#if VDRVERSNUM>=10318
	      snprintf(line1,11,"%10s",*IndexToHMSF(Current,false));
#else
	      snprintf(line1,11,"%10s",IndexToHMSF(Current,false));
#endif
	    }
	}

      /* Display Time and Progressbar */
      if (ProgressValue != OldProgressValue)
	{
	  progressbar(ProgressValue);
	  OldProgressValue=ProgressValue;
	}
      if (strcmp(line1,oldline1)!=0 || refresh)
	  {
	    print_line(0,line1);
	    for (i=0;i<12;i++)
	      {
		oldline1[i] = line1[i];
	      }
	  }
      refresh = false;
      break;  
    }
      }
    usleep(100000-(now.tv_usec%100000));
  }
  running = false;
}

