#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/time.h>
#include <termios.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <ctype.h>

#include "main.h"
#include "mode.h"
#include "../../shared/sockets.h"
#include "../../shared/debug.h"


// TODO: Commenting...  Everything!


int Quit = 0;
int sock;

char *version = VERSION;
char *build_date = __DATE__;

int lcd_wid;
int lcd_hgt;
int lcd_cellwid;
int lcd_cellhgt;


/*
  Mode List:
    See below...  (default_sequence[])
*/

void HelpScreen();
void exit_program(int val);
void main_loop(mode *sequence);

#define MAX_SEQUENCE 256
// 1/8th second is a single time unit...
#define TIME_UNIT 125000


// Contains a list of modes to run
mode default_sequence[] = 
{
// which on  off  inv  timer/visible  
  { 'C', 1,   2,   0,  0xffff,0,},// [C]PU
  { 'M', 4,   16,  0,  0xffff,0,},// [M]emory
// { 'X', 64,  128, 1,  0xffff,0,},// [X]-load (load histogram)
  { 'T', 4,   64,  0,  0xffff,0,},// [T]ime/Date
//  { 'A', 999,9999, 0,  0xffff,0,},// [A]bout (credits)

  {  1 , 0,   0,   0,  0,0,},// Modes after this line will not be run by default...
                    // ... all non-default modes must be in here!
                    // ... they will not show up otherwise.
  { 'O', 4,   64,  0,  0xffff,0,},// [O]ld Timescreen
  { 'K', 4,   64,  0,  0xffff,0,},// big cloc[K] 
  { 'U', 4,   128, 0,  0xffff,0,},// Old [U]ptime Screen
//  { 'B', 32,  256, 1,  0xffff,0,},// [B]attery Status
//  { 'G', 1,   2,   0,  0xffff,0,},// Cpu histogram [G]raph
  { 'S', 16,  256, 1,  0xffff,0,},// [S]ize of biggest programs
  { 'D', 256, 256, 1,  0xffff,0,},// [D]isk stats
  { 0,	0,    0,       0,0,},// No more..  all done.
};

// TODO: Clean up main()...  It is still way too big.

// TODO: Config file; not just command line


int main(int argc, char **argv)
{
   mode sequence[MAX_SEQUENCE];
   char cfgfile[256] = "/etc/lcdproc.cf";
   char server[256] = "localhost";
   int port = LCDPORT;
   int i, j, k;
   int tmp;

   memset(sequence, 0, sizeof(mode) * MAX_SEQUENCE);

   // Ctrl-C will cause a clean exit...
   signal(SIGINT, exit_program);
   // and "kill"...
   signal(SIGTERM, exit_program);
   // and "kill -HUP" (hangup)...
   signal(SIGHUP, exit_program);
   // and just in case, "kill -KILL" (which cannot be trapped; but oh well)
   signal(SIGKILL, exit_program);
  

  
   // Command line
   memcpy(sequence, default_sequence, sizeof(default_sequence));
   for(i=1, j=0; i<argc; i++)
   {
      if(argv[i][0] == '-') switch(argv[i][1])
      {
	 // s is for server
	 case 'S':
	 case 's': if(argc < i+1) HelpScreen();
	    strcpy(server, argv[++i]);
	    break;
	 // p is for port
	 case 'P':
	 case 'p': if(argc < i+1) HelpScreen();
	    port = atoi(argv[++i]);
	    if(port < 1  &&  port > 0xffff)
	       fprintf(stderr, "Warning:  Port %i outside of standard range\n",
		       port);
	    break;
      
	    // otherwise...  Get help!
	 default: HelpScreen(); break;
      }
      // Parse command line here...  read the man page.
      else if(strlen(argv[i]) == 1)
      {
	 // Grab the mode letter...
	 sequence[j].which = argv[i][0];

	 // If we have just a letter with no numbers...
	 // Set the defaults...
	 for(tmp=0, k=0; default_sequence[k].which; k++)
	 {
	    if(toupper(sequence[j].which) == default_sequence[k].which)
	    {
	       memcpy(&sequence[j], &default_sequence[k], sizeof(mode));
	       tmp=1;
	       break;
	    }
	 }
	 if(!tmp) { printf("Invalid Mode: %c\n", argv[i][0]); exit(0); }

	 j++;
	 // Set the last element to 0...
	 memset(sequence + j, 0, sizeof(mode));
      } // End if(strlen(argv == 1))
      else
      {
	 // A multicharacter parameter by itself
	 //  is assumed to be a config file..
	 strcpy(cfgfile, argv[i]);
	 printf("Ignoring config file: %s\n", cfgfile);
      }
   }


   // Connect to the server...
   usleep(500000); // wait for the server to start up
   sock = sock_connect(server, port);
   if (sock <= 0)
   {
      printf("Error connecting to server %s on port %i.\n",
	     server, port);
      return 0;
   }
   sock_send_string(sock, "hello\n");
   usleep(500000); // wait for the server to say hi.
   
   
   // We grab the real values below, from the "connect" line.
   lcd_wid = 20;
   lcd_hgt = 2;
   lcd_cellwid = 5;
   lcd_cellhgt = 8;
   
   // Init the status gatherers...
   mode_init(sequence);

   // And spew stuff!
   main_loop(sequence);
  
   // Clean up
   exit_program(0);
   return 0;
}


void HelpScreen()
{
  printf("lcd_stats, %s\n", version);
  printf("Usage: lcd_stats [-s server] [-p port] [modelist]\n");
  printf("\tOptions in []'s are optional.\n");
  printf("\tmodelist is \"mode [mode mode ...]\"\n");
  printf("\tMode letters: \t[C]pu [T]ime [M]emory [D]isk\n\t\t\tproc_[S]izes [O]ld_time big_cloc[K] [U]ptime\n");
  printf("\n");
  printf("Example:\n");
  printf("\tlcd_stats -s my.lcdproc.server.com C M X -p 13666\n");
  printf("\n");
  exit(0);
}


///////////////////////////////////////////////////////////////////
// Called upon TERM and INTR signals...
//
void exit_program(int val)
{
  Quit = 1;
  sock_close(sock);
  mode_close();
  exit(0);
}


///////////////////////////////////////////////////////////////////
// Main program loop...
//
void main_loop(mode *sequence)
{
   int i=0, j;
   //int status=0;
   char buf[8192];
   char *argv[256];
   int argc, newtoken;
   int len;
   
   
   // Main loop
   // Run whatever screen we want, then wait.  Woo-hoo!
   while(!Quit)
   {
      // Check for server input...
      len = sock_recv(sock, buf, 8000);
       // Handle server input...
      while(len > 0)
      {
	 // Now split the string into tokens...
	 //for(i=0; i<argc; i++) argv[i]=NULL; // Get rid of old tokens
	 argc = 0; newtoken = 1;
	 for(i=0; i<len; i++)
	 {
	    if(buf[i])    // For regular letters, keep tokenizing...
	    {
	       switch(buf[i])
	       {
		  case ' ':
		     newtoken = 1;
		     buf[i] = 0;
		     break;
		  case '\n':
		     buf[i] = 0;
		  default:
		     if(newtoken)
		     {
			argv[argc] = buf+i;
			argc++;
		     }
		     newtoken = 0;
		     break;
	       }
	    }
	    else  // If we've got a zero, it's the end of a string...
	    {
	       if(argc > 0)
	       {
		  //printf("%s %s\n", argv[0], argv[1]);
		  if(0 == strcmp(argv[0], "listen"))
		  {
		     for(j=0; sequence[j].which; j++)
		     {
			if(sequence[j].which == argv[1][0])
			{
			   sequence[j].visible = 1;
			   debug("Listen %s\n", argv[1]);
			}
		     }
		  }
		  else if(0 == strcmp(argv[0], "ignore"))
		  {
		     for(j=0; sequence[j].which; j++)
		     {
			if(sequence[j].which == argv[1][0])
			{
			   sequence[j].visible = 1;
			   debug("Ignore %s\n", argv[1]);
			}
		     }
		    	     
		  }
		  else if(0 == strcmp(argv[0], "key"))
		  {
		     debug("Key %s\n", argv[1]);
		     /* Exit the loop if key is ESC */
		     if ( strcmp (argv[1],"E") == 0)
		        exit_program(0);
		  }
		  else if(0 == strcmp(argv[0], "menu"))
		  {
		  }
		  else if(0 == strcmp(argv[0], "connect"))
		  {
		     int a;
		     for(a=1; a<argc; a++)
		     {
			if(0 == strcmp(argv[a], "wid")) {
			   lcd_wid = atoi(argv[++a]);
			   debug("Wid: %d\n", lcd_wid);
			}
			else if(0 == strcmp(argv[a], "hgt")) {
			   lcd_hgt = atoi(argv[++a]);
			   debug("Hgt: %d\n", lcd_hgt);
			}
			else if(0 == strcmp(argv[a], "cellwid")){
			   lcd_cellwid = atoi(argv[++a]);
			   debug("CellWid: %d\n", lcd_cellwid);
			}
			else if(0 == strcmp(argv[a], "cellhgt")){
			   lcd_cellhgt = atoi(argv[++a]);
			   debug("CellHgt: %d\n", lcd_cellhgt);
			}
		     }
		  }
		  else if(0 == strcmp(argv[0], "bye"))
		  {
		     //printf("Exiting LCDproc\n");
		     exit_program(0);
		  }
		  else
		  {
		     int j;
		     for(j=0; j<argc; j++)
			printf("%s ", argv[j]);
		     printf("\n");
		  }
	       }
	       
	       argc = 0;  newtoken = 1;
	    }
	 }
	 
	 len = sock_recv(sock, buf, 8000);
	 debug("sock_recv\n");
      }
      // Gather stats...
      // Update screens...
      for(i=0; sequence[i].which > 1; i++)
      {
	 sequence[i].timer++;
	 if(sequence[i].visible)
	 {
	    if(sequence[i].timer >= sequence[i].on_time)
	    {
	       sequence[i].timer = 0;
	       // Now, update the screen...
	       debug("Update visible->on_time\n");
	       update_screen(sequence+i, 1);
	    }
	 }
	
	 else
	 {
	    
	    if(sequence[i].show_invisible)
	    { 
	    
	       if(sequence[i].timer >= sequence[i].off_time)
	       {
		  sequence[i].timer = 0;
		  // Now, update the screen...
		  debug("Update show_invisible->off_time\n");
		  update_screen(sequence+i, 1);
	       }
	    }
	    else
	    {
	       if(sequence[i].timer >= sequence[i].off_time)
	       {
		  sequence[i].timer = 0;
		  // Now, update the screen...
		  debug("Update not show_invisible->off_time\n");
		  update_screen(sequence+i, 0);
	       }
	    }
	 }
	 
      }

      // Now sleep...
      usleep(TIME_UNIT);
      
/* Old stuff      
      timer=0;
      for(j=0; hold || (j<sequence[i].num_times && !Quit); j++)
      {
	 switch(sequence[i].which)
	 {
	    case 'g':
	    case 'G': status = cpu_graph_screen(j); break;
	    case 'c':
	    case 'C': status = cpu_screen(j); break;
	    case 'o':
	    case 'O': status = clock_screen(j); break;
	    case 'k':
	    case 'K': status = big_clock_screen(j); break;
	    case 'm':
	    case 'M': status = mem_screen(j); break;
	    case 'u':
	    case 'U': status = uptime_screen(j); break;
	    case 't':
	    case 'T': status = time_screen(j); break;
	    case 'd':
	    case 'D': status = disk_screen(j); break;
	    case 'x':
	    case 'X': status = xload_screen(j); break;
	    case 'b':
	    case 'B': status = battery_screen(j); break;
	    case 'a':
	    case 'A': status = credit_screen(j); break;
	    default: status = dumbass_screen(j); break;
	 }
	 
	 
	 
	 
	 for(k=0; k<sequence[i].delay_time; k++)
	 {
	    usleep(TIME_UNIT);  timer++;
	    // Modify lcd status here...  (blinking, for example)
	    switch(status)
	    {
	       case BLINK_ON : blink=1; break;
	       case BLINK_OFF: blink=0; lcd.backlight(1); break;
	       case BACKLIGHT_OFF: blink=0; lcd.backlight(0); break;
	       case BACKLIGHT_ON : blink=0; lcd.backlight(1); break;
	       case CONTINUE    : hold=0; break;
	    }
	    status=0;
	    
	    if(blink) lcd.backlight(! ((timer&15) == 15));
	    
	    if(heartbeat)
	    {
	       // Set this to pulsate like a real heart beat...
	       // (binary is fun...  :)
	       lcd.icon(!((timer+4)&5), 0);
	       lcd.chr(lcd.wid, 1, 0);
	    }
	    
	    lcd.flush();
	    
	    PollSockets();
	 }
      }
      i++;
      if(sequence[i].which < 2) i=0;
*/
   }
}



