/* tree controller #7 */
/* w\rewritten in C */
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <dos.h>
#include <dir.h>
#include <sys\timeb.h>
#include <ctype.h>
#include <commlib.h>

/* function protoypes */
void AskToExit(int x, int y);
void Box(int tr,int lc,int br,int rc,int FT,int color);
void CloseTheComPort(void);
void File_Exists(char *f,int *x);
char GetChar(void);
void GetDefaults(void);
void GetDefaultPatterns(void);
double GetTime(void);
void GetUserPatterns(void);
int  Instr(char *s,char *b);
void MarkActive(int p);
int  OneColor(int foreground,int background);
void OpenTheComPort(void);
void QPrintRC(char *str,int y,int x,int color);
void SaveDefaults(void);
void Send_All_Sockets(char *c);
void SendNextPattern(int p);
void ShowCurrentPattern(char *a);
void ShowCurrentName(int p);
void ShowDelayScreen(char *StTime);
void ShowHelpScreen(void);
void ShowInvert(void);
void ShowMainScreen(void);
void ShowPort(void);
void ShowSpeed(void);

/*            +-------- up to eight patterns               */
/*            |                                            */
/*            |  +----- up to sixty-four steps per pattern */
/*            |  |                                         */
/*            |  |   +- pattern stored here X=0, !X=off    */
/*            |  |   |                                     */
char LightPat[8][64][17];
char PatName[8][13];

/* define screen colors */
int FG = 15;
int BG = 2;
int Colour, Hilite;
struct
 {
  double PiDF;
  double PpDF;
  double PgDF;
  int Seq;
  int invert;
  int AutoMode;
  int Port;
  int PatSel;
  int AutoGrpInc;
  int dy;
  int last[10];
  int LockPattern[10];
 } var;
char File[]="TreeDflt.dat";
char cmd[6];

int savex=1;
int savey=30;

PORT *port;

#define LASTPAT 8

#define MENUX 3
#define MENUY 1
#define PATTERN_TYPE_X 41
#define AUTOMODE_X 17
#define MODE_TIMEX 9
#define PAT_TIMEX  1
#define PORTX 34
#define INVERTX 25

/*@@@@@@
@ main @
@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void main(int argc, char *argv[])
{
 int fl;
 int i;
 char tmp[81];
 char StTime[9]="00:00:00";
 char a;
 int Pattern;
 int ActivePats=0;
 struct tm *time_now;
 time_t secs_now;
 struct timeb t;
 double TimeToChangePattern;
 int lk;

  cmd[0] = 0x1b;
 cmd[1] = '0';
 cmd[4] = 'E';
 cmd[5] = 0;

 Pattern=LASTPAT;

 tzset();

 var.invert = 0;
 var.AutoMode = 0;
 var.Port = 0;

 Colour = OneColor(FG, BG);
 Hilite = OneColor(WHITE, RED);
 var.PiDF = .1;
 var.PpDF = 15.0;
 var.PgDF = 30.0;
 var.PatSel = 0;

 clrscr();

 TimeToChangePattern = GetTime()+var.PpDF;

 GetDefaults();

 if(argc>1)
  {
   for(i=1; i<argc; i++)
    {
     /********************************/
     /* user wants command line help */
     /********************************/
     if(argv[i][0] == '?' || !strncmpi(argv[i],"/H",2) || !strncmpi(argv[i],"\H",2))
      {
       ShowHelpScreen();
       _setcursortype(_NORMALCURSOR);
       exit(0);
      } /* end if */

     /*******************************/
     /* user wants new default file */
     /*******************************/
     if(!strncmpi(argv[i],"/N",2) || !strncmpi(argv[i],"\N",2))
      {
       GetDefaultPatterns();
       SaveDefaults();
      }

     /**********************************/
     /* user specifies filename to use */
     /**********************************/
     if(!strncmpi(argv[i],"/F",2) || !strncmpi(argv[i],"\F",2))
      {
       fl=0;
       do
        {
         File[fl]=argv[i][fl+2];
        } while(argv[i][fl+2]);
      } /* end if */

     /**************************************/
     /* see if we should start in automode */
     /**************************************/
     if(!strncmpi(argv[i],"/A",2) || !strncmpi(argv[i],"\A",2))
      {
       var.AutoMode=1;
       Pattern=0;
      }

     /*****************************************/
     /* see if we should start in manual mode */
     /*****************************************/
     if(!strncmpi(argv[i],"/M",2) || !strncmpi(argv[i],"\M",2))
      Pattern = argv[i][2]-'1';

     /*******************/
     /* get serial port */
     /*******************/
     if(!strncmpi(argv[i],"/P1",3) || !strncmpi(argv[i],"\P1",3)) var.Port = 0;
     if(!strncmpi(argv[i],"/P2",3) || !strncmpi(argv[i],"\P2",3)) var.Port = 1;
     if(!strncmpi(argv[i],"/P3",3) || !strncmpi(argv[i],"\P3",3)) var.Port = 2;
     if(!strncmpi(argv[i],"/P4",3) || !strncmpi(argv[i],"\P4",3)) var.Port = 3;

     /****************************/
     /* set up for delayed start */
     /****************************/
     if(!strncmpi(argv[i], "/S", 2) || !strncmpi(argv[i], "\S", 2))
      {
       fl=0;
       do
        {
         if(fl<2)
          StTime[fl]=argv[i][fl+2];
         else
          StTime[fl+1]=argv[i][fl+2];
         fl++;
        } while(argv[i][fl+1]);
       strcat(StTime,":00");
       ShowDelayScreen(StTime);

       do
        {
         ftime(&t);
         time(&secs_now);
         time_now = localtime(&secs_now);
         strftime(tmp, 9, "%X:%S", time_now);
         QPrintRC(tmp, 12, 16, Colour);
         if(GetChar()=='Q')
          {
           AskToExit(1, 1);
           ShowDelayScreen(StTime);
          }
        } while(strcmp(StTime,tmp));
      } /* end if */
    } /* next i */
  } /* end if */
 textmode(C80);

 GetDefaultPatterns();

 if(var.PatSel)
  {
   GetUserPatterns();
   QPrintRC("User Defd", MENUY+3, MENUX+PATTERN_TYPE_X, Hilite);
  }
 else
  {
   QPrintRC("Built In ", MENUY+3, MENUX+PATTERN_TYPE_X, Hilite);
  }

 ShowInvert();
 ShowCurrentName(Pattern);

 if(var.AutoMode || Pattern<LASTPAT)
  {
   MarkActive(Pattern);
  }

 ActivePats = 0;
 for(lk=0; lk<LASTPAT; lk++)
  {
   if(var.LockPattern[lk])
    {
     ActivePats++;
    }
  } /* NEXT lk */

 TimeToChangePattern = GetTime()+var.PpDF;
 _setcursortype(_NOCURSOR);

 window(1, MENUY+12, 80, 25);

 OpenTheComPort();

 do
  {
   a=GetChar();
   switch(a)
   {
    /********************/
    /* Select a pattern */
    /********************/
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
     Pattern = a-'1';
     var.Seq = 1;
     MarkActive(Pattern);
     break;

    /*******************/
    /* toggle automode */
    /*******************/
    case 'M':
     var.AutoMode = !var.AutoMode;
     Pattern = 0;
     MarkActive(Pattern);
     if(var.AutoMode)
      {
       MarkActive(Pattern);
       TimeToChangePattern = GetTime()+var.PpDF;
       ShowSpeed();
      }
     break;

    /**************************/
    /* decrease time per step */
    /**************************/
    case '_':
    case '-':
     var.PiDF -= 0.05;
     if(var.PiDF < 0.0) var.PiDF = 0.0;
     ShowSpeed();
     break;

    /**************************/
    /* increase time per step */
    /**************************/
    case '+':
    case '=':
     var.PiDF = var.PiDF + 0.05;
     if(var.PiDF > 99.0) var.PiDF = 99.0;
     ShowSpeed();
     break;

    /*****************************/
    /* decrease time per pattern */
    /*****************************/
    case ',':
    case '<':
     var.PpDF = var.PpDF - 1.0;
     if(var.PpDF < 0.0) var.PpDF = 0.0;
     TimeToChangePattern = GetTime() + var.PpDF;
     ShowSpeed();
     break;

    /*****************************/
    /* increase time per pattern */
    /*****************************/
    case '.':
    case '>':
     var.PpDF = var.PpDF + 1.0;
     if(var.PpDF > 999) var.PpDF = 999;
     TimeToChangePattern = GetTime() + var.PpDF;
     ShowSpeed();
     break;

    /*****************/
    /* toggle invert */
    /*****************/
    case 'I':
     var.invert = !var.invert;
     ShowInvert();
     break;

    /************************/
    /* toggle user patterns */
    /************************/
    case 'U':
     var.PatSel = !var.PatSel;
     if(var.PatSel)
      {
       GetUserPatterns();
       QPrintRC("User Def", MENUY+3, MENUX+PATTERN_TYPE_X, Hilite);
      }
     else
      {
       GetDefaultPatterns();
       QPrintRC("Built In", MENUY+3, MENUX+PATTERN_TYPE_X, Hilite);
      }
     break;

    /************************/
    /* turn all outputs Off */
    /************************/
    case 'O':
     Pattern = LASTPAT;
     MarkActive(Pattern);
     QPrintRC("Off ", 48, 38, Colour);
     break;

    /**********************/
    /* change serial Port */
    /**********************/
    case 'P':
     var.Port++;
     if(var.Port>3)
      var.Port = 0;
     ShowPort();
     OpenTheComPort();
     break;

    case '!':
    case '@':
    case '#':
    case '$':
    case '%':
    case '^':
    case '&':
	case '*': /* shift 1 to 8 */
    switch(a)
    {
     case '!': var.LockPattern[0] = !var.LockPattern[0]; break; /*1 */
     case '@': var.LockPattern[1] = !var.LockPattern[1]; break; /*2 */
     case '#': var.LockPattern[2] = !var.LockPattern[2]; break; /*3 */
     case '$': var.LockPattern[3] = !var.LockPattern[3]; break; /*4 */
     case '%': var.LockPattern[4] = !var.LockPattern[4]; break; /*5 */
     case '^': var.LockPattern[5] = !var.LockPattern[5]; break; /*6 */
     case '&': var.LockPattern[6] = !var.LockPattern[6]; break; /*7 */
     case '*': var.LockPattern[7] = !var.LockPattern[7]; break; /*8 */
    } /* END switch */
   ShowCurrentName(Pattern);
   ActivePats = 0;
   for(lk=0; lk<LASTPAT; lk++)
    {
     if(var.LockPattern[lk])
      {
       ActivePats++;
      }
    } /* NEXT lk */
   break;

  case 'Q':
   AskToExit(MENUX+36, MENUY+10);
   MarkActive(Pattern);
   break;
  } /* eND switch */

 if(var.AutoMode)
  {
   if(GetTime() >= TimeToChangePattern)
    {
     TimeToChangePattern = GetTime() + var.PpDF;
     do
      {
       Pattern = rand() % LASTPAT;
       var.invert = rand() % 2;
      } while(!var.LockPattern[Pattern] && ActivePats);
     ShowInvert();
     var.Seq = 0;
     MarkActive(Pattern);
    }
   else
    {
     sprintf(tmp,"%3.0f",TimeToChangePattern - GetTime());
     QPrintRC(tmp,MENUY+3,MENUX+AUTOMODE_X+4,Colour);
    } /* end if */
  } /* end if */
 SendNextPattern(Pattern);
} while(1);/* LOOP */
} /* end function  */

/*@@@@@@@@@@@
@ AskToExit @
@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void AskToExit(int x, int y)
{
 int s;
 char a;

 for(s=1000; s<2001; s+=100)
  {
   sound(s);
   delay(10);
  }
 nosound();
 QPrintRC("Press \"Y\" to quit", y, x, Hilite);
 QPrintRC("Y", y, x+7, Hilite + 128);
 do
  {
   a = GetChar();
  } while(!a);

 if(a == 'Y')
  {
   Send_All_Sockets("................");
   SaveDefaults();
   textmode(C80);
   _setcursortype(_NORMALCURSOR);
   CloseTheComPort();
   exit(0);
  }
 QPrintRC("                 ", y, x, Colour);
} /* end function AskToExit */

/*@@@@@@@@@@@
@ DelayTime @
@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void DelayTime(void)
{
 double dt;

 dt = GetTime() + var.PiDF;
 while(GetTime() < dt);
} /* end function DelayTime */

/*@@@@@@@@@@@@@@@@@@@@
@ GetDefaultPatterns @
@@@@@@@@@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void GetDefaultPatterns(void)
{
 int p,s;
 
 ShowMainScreen();

 /* pattern 1 */
 p=0;
 s=0;
 strcpy(PatName[p],"Forward");

 strcpy(LightPat[p][s++],"X...............");
 strcpy(LightPat[p][s++],".X..............");
 strcpy(LightPat[p][s++],"..X.............");
 strcpy(LightPat[p][s++],"...X............");
 strcpy(LightPat[p][s++],"....X...........");
 strcpy(LightPat[p][s++],".....X..........");
 strcpy(LightPat[p][s++],"......X.........");
 strcpy(LightPat[p][s++],".......X........");
 strcpy(LightPat[p][s++],"........X.......");
 strcpy(LightPat[p][s++],".........X......");
 strcpy(LightPat[p][s++],"..........X.....");
 strcpy(LightPat[p][s++],"...........X....");
 strcpy(LightPat[p][s++],"............X...");
 strcpy(LightPat[p][s++],".............X..");
 strcpy(LightPat[p][s++],"..............X.");
 strcpy(LightPat[p][s],  "...............X");
 var.last[p]=s;

 /* pattern 2 */
 p=1;
 s=0;
 strcpy(PatName[p],"Reverse");

 strcpy(LightPat[p][s++],"...............X");
 strcpy(LightPat[p][s++],"..............X.");
 strcpy(LightPat[p][s++],".............X..");
 strcpy(LightPat[p][s++],"............X...");
 strcpy(LightPat[p][s++],"...........X....");
 strcpy(LightPat[p][s++],"..........X.....");
 strcpy(LightPat[p][s++],".........X......");
 strcpy(LightPat[p][s++],"........X.......");
 strcpy(LightPat[p][s++],".......X........");
 strcpy(LightPat[p][s++],"......X.........");
 strcpy(LightPat[p][s++],".....X..........");
 strcpy(LightPat[p][s++],"....X...........");
 strcpy(LightPat[p][s++],"...X............");
 strcpy(LightPat[p][s++],"..X.............");
 strcpy(LightPat[p][s++],".X..............");
 strcpy(LightPat[p][s],  "X...............");
 var.last[p]=s;

 /* pattern 3 */
 p=2;
 s=0;
 strcpy(PatName[p],"Bounce");

 strcpy(LightPat[p][s++],"X...............");
 strcpy(LightPat[p][s++],".X..............");
 strcpy(LightPat[p][s++],"..X.............");
 strcpy(LightPat[p][s++],"...X............");
 strcpy(LightPat[p][s++],"....X...........");
 strcpy(LightPat[p][s++],".....X..........");
 strcpy(LightPat[p][s++],"......X.........");
 strcpy(LightPat[p][s++],".......X........");
 strcpy(LightPat[p][s++],"........X.......");
 strcpy(LightPat[p][s++],".........X......");
 strcpy(LightPat[p][s++],"..........X.....");
 strcpy(LightPat[p][s++],"...........X....");
 strcpy(LightPat[p][s++],"............X...");
 strcpy(LightPat[p][s++],".............X..");
 strcpy(LightPat[p][s++],"..............X.");
 strcpy(LightPat[p][s++],"...............X");
 strcpy(LightPat[p][s++],"...............X");
 strcpy(LightPat[p][s++],"..............X.");
 strcpy(LightPat[p][s++],".............X..");
 strcpy(LightPat[p][s++],"............X...");
 strcpy(LightPat[p][s++],"...........X....");
 strcpy(LightPat[p][s++],"..........X.....");
 strcpy(LightPat[p][s++],".........X......");
 strcpy(LightPat[p][s++],"........X.......");
 strcpy(LightPat[p][s++],".......X........");
 strcpy(LightPat[p][s++],"......X.........");
 strcpy(LightPat[p][s++],".....X..........");
 strcpy(LightPat[p][s++],"....X...........");
 strcpy(LightPat[p][s++],"...X............");
 strcpy(LightPat[p][s++],"..X.............");
 strcpy(LightPat[p][s++],".X..............");
 strcpy(LightPat[p][s],  "X...............");
 var.last[p]=s;

 /* pattern 4 */
 p=3;
 s=0;
 strcpy(PatName[p],"Back & Forth");

 strcpy(LightPat[p][s++],"X..............X");
 strcpy(LightPat[p][s++],".X............X.");
 strcpy(LightPat[p][s++],"..X..........X..");
 strcpy(LightPat[p][s++],"...X........X...");
 strcpy(LightPat[p][s++],"....X......X....");
 strcpy(LightPat[p][s++],".....X....X.....");
 strcpy(LightPat[p][s++],"......X..X......");
 strcpy(LightPat[p][s++],".......XX.......");
 strcpy(LightPat[p][s++],".......XX.......");
 strcpy(LightPat[p][s++],"......X..X......");
 strcpy(LightPat[p][s++],".....X....X.....");
 strcpy(LightPat[p][s++],"....X......X....");
 strcpy(LightPat[p][s++],"...X........X...");
 strcpy(LightPat[p][s++],"..X..........X..");
 strcpy(LightPat[p][s++],".X............X.");
 strcpy(LightPat[p][s],  "X..............X");
 var.last[p]=s;

 /* pattern 5 */
 p=4;
 s=0;
 strcpy(PatName[p],"Spaz");

 strcpy(LightPat[p][s++],".X.X.X.X.X.X.X.X");
 strcpy(LightPat[p][s],  "X.X.X.X.X.X.X.X.");
 var.last[p]=s;

 /* pattern 6 */
 p=5;
 s=0;
 strcpy(PatName[p],"Fwd Marquee");

 strcpy(LightPat[p][s++],"X...X...X...X...");
 strcpy(LightPat[p][s++],".X...X...X...X..");
 strcpy(LightPat[p][s++],"..X...X...X...X.");
 strcpy(LightPat[p][s++],"...X...X...X...X");
 strcpy(LightPat[p][s++],"X...X...X...X...");
 strcpy(LightPat[p][s++],".X...X...X...X..");
 strcpy(LightPat[p][s++],"..X...X...X...X.");
 strcpy(LightPat[p][s],  "...X...X...X...X");
 var.last[p]=s;

 /* pattern 7 */
 p=6;
 s=0;
 strcpy(PatName[p],"Rev Marquee");

 strcpy(LightPat[p][s++],"X...X...X...X...");
 strcpy(LightPat[p][s++],"...X...X...X...X");
 strcpy(LightPat[p][s++],"..X...X...X...X.");
 strcpy(LightPat[p][s++],".X...X...X...X..");
 strcpy(LightPat[p][s++],"X...X...X...X...");
 strcpy(LightPat[p][s++],"...X...X...X...X");
 strcpy(LightPat[p][s++],"..X...X...X...X.");
 strcpy(LightPat[p][s],  ".X...X...X...X..");
 var.last[p]=s;

 strcpy(PatName[LASTPAT-1],"random?");
 var.last[LASTPAT-1]=1;

 ShowCurrentName(LASTPAT);
} /* end function GetDefaultPatterns */

/*@@@@@@@@@@@@@
@ GetDefaults @
@@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void GetDefaults(void)
{
 int n;
 FILE *fp;
 File_Exists(File,&n);
 if(n)
  {
   fp=fopen(File, "rb");
   fread(&var, sizeof(var), 1, fp);
   fclose(fp);
  } /* endif */
} /* end function GetDefaults */

/*@@@@@@@@@@@@@@@@@
@ GetUserPatterns @
@@@@@@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void GetUserPatterns(void)
{
 int x,p,s,i;
 int done;
 char tmp[35];
 char ch;
 FILE *fp;


 File_Exists("PATTERN.DAT",&x);
 if(x)
  {
   fp=fopen("PATTERN.DAT","rt");

   /* for each of the eight patterns */
   for(p=0; p<LASTPAT && !feof(fp); p++)
    {
     x = 40;
     i = 0;
     do
      {
       ch = getc(fp);
       if(ch != '\n' && ch != '\r')
        {
         tmp[i++] = ch;
        }
      } while(ch != '\n' && ch != '\r');
     tmp[i] = '\0';
     if(strcmpi(tmp, "end"))
      {
       strcpy(PatName[p], tmp);
       s=0;
       done=0;
       do
        {
         fscanf(fp,"%s\n", tmp);
         strupr(tmp);
         if(strcmp(tmp,"LAST"))
          {
           strcpy(LightPat[p][s], tmp);
           var.last[p]=s;
           s++;
          }
         else
          {
           done=1;
          }
        } while(!done);
      }
     else
      {
       break;
      }
    } /* next p */
   fclose(fp);
  } /* end if data file found */
} /* end function GetUserPatterns */

/*@@@@@@@@@@@@@@@@@@@
@ MakeRandomPattern @
@@@@@@@@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void MakeRandomPattern(void)
{
 int p = LASTPAT-1;

 for(p=0; p<16; p++)
  {
   if(random(2) == 1)
    {
     LightPat[LASTPAT-1][0][p] = 'X';
    }
   else
    {
     LightPat[LASTPAT-1][0][p] = '.';
    }
  }
 LightPat[LASTPAT-1][0][p] = 0;
 var.Seq=0;
} /* end function MakeRandomPattern */


/*@@@@@@@@@@@@
@ MarkActive @
@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void MarkActive(int p)
{
 int x=MENUX+AUTOMODE_X;
 int y=MENUY+3;

 ShowCurrentName(p);

 window(1,1,80,25);

 if(var.AutoMode)
  {
   QPrintRC("Auto", y, x, Hilite);
  }
 else
  {
   QPrintRC("Manual ", y, x, Hilite);
  }
 window(1, MENUY+12, 80, 25);
} /* end function MarkActive */

/*@@@@@@@@@@@@@@@@@
@ ShowCurrentName @
@@@@@@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void ShowCurrentName(int p)
{
 char tmp[20];
 int i;
 int x=MENUX+62;
 int y=MENUY+1;

 window(1,1,80,25);

 for(i=0; i<LASTPAT; i++)
  {
   if(var.LockPattern[i])
    {
     sprintf(tmp,"û%d-%-12.12s",i+1,PatName[i]);
    }
   else
    {
     sprintf(tmp," %d-%-12.12s",i+1,PatName[i]);
    }

   if(i==p)
    {
     QPrintRC(tmp,y+i,x,Hilite);
    }
   else
    {
     QPrintRC(tmp,y+i,x,Colour);
    }
  }
 window(1, MENUY+12, 80, 25);
} /* end function ShowCurrentName */


/*@@@@@@@@@@@@@@
@ SaveDefaults @
@@@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void SaveDefaults(void)
{
 FILE *fp;
 
 fp = fopen(File,"wb");
 fwrite(&var,sizeof(var),1,fp);
 fclose(fp);
} /* end function SaveDefaults */

/*@@@@@@@@@@@@@@@@@
@ SendNextPattern @
@@@@@@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void SendNextPattern(int p)
{
 if(p==LASTPAT)
  {
   if(var.invert)
    {
     Send_All_Sockets("XXXXXXXXXXXXXXXX");
    }
   else
    {
     Send_All_Sockets("................");
    }
  }

 if(p == LASTPAT-1)
  MakeRandomPattern();

 if(p<LASTPAT)
  {
   ShowCurrentPattern(LightPat[p][var.Seq]);       // show in scroll
  }

 var.dy=(++var.dy) % 30;

 Send_All_Sockets(LightPat[p][var.Seq]);
 if(p >= 0 && p <= LASTPAT)
  var.Seq++;


 if(var.Seq > var.last[p])
  var.Seq = 0;

 DelayTime();
} /* end function SendNextPattern */

/*@@@@@@@@@@@@@@@@@@
@ Send_All_Sockets @
@@@@@@@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void Send_All_Sockets(char *s)
{
 int i;

 for(i=0; i<16; i++)
  {
   if(i<10)
    cmd[2] = i + '0';
   else
    cmd[2] = i + 'A' - 10;

   if(var.invert)
    {
     if(s[i] == 'X')
      {
       cmd[3] = '0';
      }
     else
      {
       cmd[3] = '1';
      }
    }
   else
    {
     if(s[i] == 'X')
      {
       cmd[3] = '1';
      }
     else
      {
       cmd[3] = '0';
      }
    }
   WriteBuffer(port,cmd,5);
  } // next i
} /* end function Send_All_Sockets */


/*@@@@@@@@@@@@@@@@@@@@
@ ShowCurrentPattern @
@@@@@@@@@@@@@@@@@@@@@@
|      Purpose: shows pattern in lower right corner
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void ShowCurrentPattern(char *a)
{
 int j;
 int x=MENUX+1;
 int y=MENUY+9;

 window(1, 1, 80, 25);
 textattr(Colour);
 for(j=0; j<16; j++)
  {
   gotoxy(x + (j<<1), y);
   if(var.invert)
    {
     if(a[j] == 'X')
      putch('.');
     else
      putch('Û');
    }
   else
    {
     if(a[j] == 'X')
      putch('Û');
     else
      putch('.');
    }
  } /* next j */

 window(1, MENUY+12, 80, 25);
 gotoxy(savex, savey);
 cputs(a);
 cputs("\r\n");
 savex=wherex();
 savey=wherey();
} /* end function ShowCurrentPattern */

/*@@@@@@@@@@@@@@@@
@ ShowMainScreen @
@@@@@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void ShowMainScreen(void)
{
 int y;
 int x;

 window(1,1,80,25);
 textattr(OneColor(FG, BG));
 clrscr();

 /* pattern name box */
 Box(MENUY, MENUX+59,MENUY+9,MENUX+77, 2, Hilite);
 QPrintRC("µ Patterns Æ",MENUY,MENUX+63,Hilite);
 QPrintRC("Shift-Num to mark",MENUY+9,MENUX+60,Hilite);

 y = MENUY+5;
 x = MENUX;
 QPrintRC("ÚÄÂÄÂÄÂÄÂÄÂÄÂÄÂÄÂÄÂÄÂÄÂÄÂÄÂÄÂÄÂÄ¿", y, x, Colour); y++;
 QPrintRC("³0³0³0³0³0³0³0³0³0³1³1³1³1³1³1³1³ Socket", y, x, Colour); y++;
 QPrintRC("³1³2³3³4³5³6³7³8³9³0³1³2³3³4³5³6³ Number", y, x, Colour); y++;
 QPrintRC("ÃÄÅÄÅÄÅÄÅÄÅÄÅÄÅÄÅÄÅÄÅÄÅÄÅÄÅÄÅÄÅÄ´", y, x, Colour); y++;
 QPrintRC("³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ Current step", y, x, Colour); y++;
 QPrintRC("ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ", y, x, Colour); y++;
 QPrintRC("ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ", y, x, Colour);

 y = MENUY;
 QPrintRC("ÉÍÍÍÍÑÍÍËÍÍÍÍÑÍÍËÍÍÍÍÍÑÍËÍÍÍÍÍÍÑÍËÍÍÍÍÑÍËÍÍÍÍÍÍÍÑÍ»", y, x, Colour); y++;
 QPrintRC("ºStep³+\x18ºPatn³>\x18ºMode ³MºInvert³IºPort³PºPattern³Uº", y, x, Colour); y++;
 QPrintRC("ºTime³-\x19ºTime³<\x19º     ÀÄ¶      ÀÄ¶    ÀÄ¶SelectnÀÄ¶", y, x, Colour); y++;
 QPrintRC("º    ÀÄÄ¶    ÀÄÄ¶       º        º      º         º", y, x, Colour); y++;
 QPrintRC("ÈÍÍÍÍÍÍÍÊÍÍÍÍÍÍÍÊÍÍÍÍÍÍÍÊÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÊÍÍÍÍÍÍÍÍÍ¼", y, x, Colour); y++;

       // QPrintRC("ºTime (Secs)³<\x19ºChg³-\x19º    ÀÄ¶   ÀÄ¶    ÀÄ¶    ÀÄ¶", y, x, Colour); y++;

 ShowSpeed();

 ShowPort();

 if(var.AutoMode)
  {
   ShowSpeed();
  } /* end if */
} /* end function ShowMainScreen */

/*@@@@@@@@@@@
@ ShowSpeed @
@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void ShowSpeed(void)
{
 int x=MENUX+MODE_TIMEX;
 int y=MENUY+3;
 char tmp[10];

 window(1, 1, 80, 25);
 sprintf(tmp,"%3.0f",var.PpDF);
 QPrintRC(tmp, y, x, Hilite);

 x=MENUX+PAT_TIMEX;
 sprintf(tmp,"%4.2f",var.PiDF);
 QPrintRC(tmp, y, x, Hilite);

 window(1, MENUY+12, 80, 25);
} /* end function ShowSpeed */


/*@@@@@@@@@@
@ OneColor @
@@@@@@@@@@@@
|      Purpose: convert a foreground/background color pair to a single number of ease of use
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
int OneColor(int foreground,int background)
{
 /* the foreground will be stored in the low nybble and the background in the next to the lowest */

 return ((background & 0xf) << 4) | (foreground & 0xf);
} /* end function OneColor */

/*@@@@@@@@@@@@@@@@
@ ShowHelpScreen @
@@@@@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void ShowHelpScreen(void)
{
 clrscr();
 puts("Welcome to the Light Controller");
 puts("------------------------------------\n");

 puts("The command line parameters are:\n");

 puts("/Px               where x is the desired port (this will override");
 puts("                  the port in the default file)\n");

 puts("/Fyyyyyy.yyy     where yyyyyy.yyy is the file name for");
 puts("                  the default settings.  You should use a different");
 puts("                  for each copy of this program you run with\n");

 puts("/Shh:mm          where hh:mm is the time for the program to begin");
 puts("                  (24 hour time)\n");

 puts(" NOTE: There is NO space after the /P or /F.  e.g. If the file name is");
 puts("       \"OUTSIDE.DFT\" then the entry would be /FOUTSIDE.DFT\n");

 puts("/Mn              Start in manual mode with pattern n\n");

 puts("/A               Start in automode\n");

 puts("/N               Make New default file.  Use this if the file");
 puts("                  gets corrupted.  This will cause the program");
 puts("                  to create a new default file.\n");

 puts("(Press any key to exit to DOS)");
 getch();
} /* end function ShowHelpScreen */

/*@@@@@@@@@@
@ QPrintRC @
@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void QPrintRC(char *str,int y,int x,int color)
{
 int h,v;

 window(1, 1, 80, 25);
 /* save window coordinates */
 h=wherex();
 v=wherey();

 /* set new color */
 textattr(color);
 gotoxy(x,y);
 cputs(str);

 /* restore cursor */
 gotoxy(h,v);

 window(1, MENUY+12, 80, 25);
} /* end function QPrintRC */

/*@@@@@@@@@
@ GetChar @
@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
char GetChar(void)
{
 if(kbhit())
  return toupper(getch());
 else
  return 0;
} /* end function GetChar */

/*@@@@@
@ Box @
@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void Box(int toprow,int leftcol,int bottomrow,int rightcol,int FrameType,int color)
{
 /* draw a Box at the specified co-ordinates in one of the following patterns:
 1= single horizontal and vertical
 2= double horizontal and vertical
 3= double horizontal,single vertical
 4= single horizontal,double vertical

 all others,use the FrameType character for the frame (eg 65= use cap. A)
 */

 char ulc; /* upper left corner  */
 char urc; /* upper right corner */
 char llc; /* lower left corner  */
 char lrc; /* lower right corner */
 char hm;  /* horizontal members */
 char vm;  /* vertical members   */

 int i;

 int h,v;

 /* save window coordinates */
 h=wherex();
 v=wherey();

 textattr(color);

 switch(FrameType)
 {
  case 1: ulc='Ú'; urc='¿'; llc='À'; lrc='Ù'; hm='Ä'; vm='³'; break;
  case 2: ulc='É'; urc='»'; llc='È'; lrc='¼'; hm='Í'; vm='º'; break;
  case 3: ulc='Õ'; urc='¸'; llc='Ô'; lrc='¾'; hm='Í'; vm='³'; break;
  case 4: ulc='Ö'; urc='·'; llc='Ó'; lrc='½'; hm='Ä'; vm='º'; break;
  default: ulc=FrameType; urc=FrameType; llc=FrameType;
   lrc=FrameType; hm=FrameType; vm=FrameType; break;
 }

 /* place the corners */
 gotoxy(leftcol,toprow);     cprintf("%c",ulc);     /* upper left corner */
 gotoxy(rightcol,toprow);    cprintf("%c",urc);    /* upper right corner */
 gotoxy(leftcol,bottomrow);  cprintf("%c",llc);  /* lower left corner */
 gotoxy(rightcol,bottomrow); cprintf("%c",lrc); /* lower right corner */

 /* draw the vertical lines */
 for(i=toprow+1; i<bottomrow; i++)
  {
   gotoxy(leftcol,i);  cprintf("%c",vm);
   gotoxy(rightcol,i); cprintf("%c",vm);
  }

 /* draw the horizontal lines */
 for(i=leftcol+1; i<rightcol; i++)
  {
   gotoxy(i,toprow);    cprintf("%c",hm);
   gotoxy(i,bottomrow); cprintf("%c",hm);
  }

 /* restore cursor */
 gotoxy(h,v);
} /* end function Box */

/*@@@@@@@@@@@@@
@ File_Exists @
@@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void File_Exists(char *f,int *x)
{
 /* return a TRUE if the requested file "f" File_Existss */
 struct ffblk ffblk;
 *x= (findfirst(f,&ffblk,0)==0);
} /* end function File_Exists */

/*@@@@@@@@@
@ GetTime @
@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
double GetTime(void)
{
 struct timeb t;
 ftime(&t);
 return (double)t.time+(double)t.millitm/1000.0;
} /* end function GetTime */

/*@@@@@@@@@@@@@@@@@
@ ShowDelayScreen @
@@@@@@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void ShowDelayScreen(char *StTime)
{
 char tmp[81];

 textmode(C40);
 _setcursortype(_NOCURSOR);
 textattr(OneColor(FG, BG));
 clrscr();
 sprintf(tmp, "Tree Light will begin at %s", StTime);
 QPrintRC(tmp, 8, 5, Colour);

 QPrintRC("Current Time", 10, 14, Colour);
 QPrintRC("ÉÍÍÍÍÍÍÍÍÍÍ»", 11, 14, Colour);
 QPrintRC("º xx:xx:xx º", 12, 14, Colour);
 QPrintRC("ÈÍÍÍÍÍÍÍÍÍÍ¼", 13, 14, Colour);

 QPrintRC("('Q' to abort to DOS)", 15, 10, Colour);
} /* end function ShowDelayScreen */

/*@@@@@@@@@@
@ ShowPort @
@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void ShowPort(void)
{
 char tmp[10];
 int x=MENUX+PORTX;
 int y=MENUY+3;

 sprintf(tmp,"Com: %d",var.Port+1);
 QPrintRC(tmp, y, x, Hilite);
} /* end function ShowPort */

/*@@@@@@@@@@@@
@ ShowInvert @
@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void ShowInvert(void)
{
 int x=MENUX+INVERTX;
 int y=MENUY+3;

 if(var.invert)
  QPrintRC("Inverted", y, x, Hilite);
 else
  QPrintRC("NotInvtd", y, x, Hilite);


 x=MENUX+AUTOMODE_X;
 if(var.AutoMode)
  {
   QPrintRC("Auto", y, x, Hilite);
  }
 else
  {
   QPrintRC("    ", y-1, x, Colour);
   QPrintRC("Manual", y, x, Hilite);
  }
} /* end function ShowInvert */

/*@@@@@@@@@@@@@@@@
@ OpenTheComPort @
@@@@@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void OpenTheComPort(void)
{
 CloseTheComPort();
 port = PortOpenGreenleaf(var.Port, 9600L, 'N', 8, 1);
} /* end function OpenTheComPort */

/*@@@@@@@@@@@@@@@@@
@ CloseTheComPort @
@@@@@@@@@@@@@@@@@@@
|      Purpose:
|        Takes:
|         Uses:
|      Returns:
| Side Effects:
|      Comment:
+---------------------------------------------------------------------------*/
void CloseTheComPort(void)
{
 if(port)
  PortClose(port); //Just close the Serial I/O
 port = NULL;
} /* end function CloseTheComPort */
