/*
 *  CELLS       An Implementation of the WireWorld cellular automata
 *              as described in Scientific American, Jan 1990.
 *
 *              Copyright 1990 by Davide P. Cervone.
 *  You may use this code, provided this copyright notice is kept intact.
 *  See the CELLS.HELP file for complete information on distribution conditions.
 */

/*
 *  File:  cScreen.c        Handles screen updates and scrolls.
 */


#include "Cells.h"
#undef INTUITION_PREFERENCES_H
#include <intuition/preferences.h>
#include <stdio.h>


#define COLORFILE       "Cells.Colors"
#define WBCOLOR         0xFFFF
#define MAXLINE         132

extern UWORD GetRGB4();


/*
 *  ClearPatch()
 *
 *  Clear the specified section of screen to the shadow color
 *  Draw the horizontal and vertical grid lines
 */

void ClearPatch(X,Y,W,H)
short X,Y,W,H;
{
   register short i;
   short x,y,w,h;
   
   x = X * CellSize + BoardX + 1;
   y = Y * CellSize + BoardY + 1;
   w = W * CellSize + x - 2;
   h = H * CellSize + y - 2;

   SetAPen(rp,SHADOW);
   RectFill(rp,x,y,w,h);
   SetAPen(rp,GRID);

   W += X; H += Y;
   for (i=Y+1, Y=y+CellSize-1; i<H; i++,Y+=CellSize)
   {
      Move(rp,x,Y);
      Draw(rp,w,Y);
   }
   for (i=X+1, X=x+CellSize-1; i<W; i++,X+=CellSize)
   {
      Move(rp,X,y);
      Draw(rp,X,h);
   }
}


/*
 *  ClearScreen()
 *
 *  Clear the board area to backgound, then clear the grid to black
 *  Draw the border around the grid area
 *  Update the screen from the double buffer, if necessary.
 */

void ClearScreen(update)
int update;
{
   SetDrMd(rp,JAM1);
   SetAPen(rp,FOREGROUND);
   RectFill(rp,0,0,BOARDW-1,BOARDH-1);
   ClearPatch(0,0,GridW,GridH);
   Move(rp,BoardX,BoardY);
   Draw(rp,BoardX,BoardH+1);
   Draw(rp,BoardW+1,BoardH+1);
   Draw(rp,BoardW+1,BoardY);
   Draw(rp,BoardX,BoardY);
   if (update) UpdateScreen();
}


/*
 *  ScrollScreen()
 *
 *  Scroll the grid area by the specified amount, clearing the new area
 *  If there was a move in the X direction, refresh the frame for that patch
 *  If there was a move in the Y direction, refresh the frame for that path
 */

void ScrollScreen(dx,dy)
short dx,dy;
{
   register short x,y;

   SetDrMd(rp,JAM1);
   SetAPen(rp,BACKGROUND);
   SetBPen(rp,SHADOW);
   dx *= CellSize;
   dy *= CellSize;
   ScrollRaster(rp,dx,dy,BoardX,BoardY,BoardW+1,BoardH+1);
   if (dx)
   {
      x = (dx < 0)? BoardX: BoardW+1;
      Move(rp,x-dx,BoardY);
      Draw(rp,x,BoardY);
      Draw(rp,x,BoardH+1);
      Draw(rp,x-dx,BoardH+1);
   }
   if (dy)
   {
      y = (dy < 0)? BoardY: BoardH+1;
      Move(rp,BoardX,y-dy);
      Draw(rp,BoardX,y);
      Draw(rp,BoardW+1,y);
      Draw(rp,BoardW+1,y-dy);
   }
}


/*
 *  RefreshWithSelect()
 *
 *  For each cell in the selected patch
 *    If we are moving or copying and the cell is selected
 *      set the pen according to whether the selection is being dragged or not
 *    Otherwise set the pen to the unselected cell color
 *    If the pen is not the blank color, refresh it on screen
 *    If we are selecing cells, and the cell is selected, and we are not 
 *      moving or copying cells, draw a box around the cell
 */

void RefreshWithSelect(x,y,w,h,Grid)
short x,y,w,h;
UBYTE Grid[];
{
   register short X,Y;
   register short i;
   register int Pen;
   int MoveCopy = (MouseMoveType == MM_MOVE || MouseMoveType == MM_COPY);

   for (Y=y; Y<y+h; Y++)
   {
      for (X=x, i=GridX+X+MAXGRIDW*(GridY+Y); X<x+w; X++,i++)
      {
         if (MoveCopy && CellSelected(i))
            Pen = (SelectType == SEL_WAIT)? SELECTPEN: MOVEPEN;
           else
            Pen = Grid[i];
         if (Pen != BLANK) ColorScreenCell(X,Y,Pen);
         if (SelectType && NewGen[i] && MoveCopy == FALSE)
            BoxCell(rp,X,Y,MOVEPEN);
      }
   }
}


/*
 *  RefreshNoSelect()
 *
 *  For each cell in the selected patch
 *    if the cell is not blank, set the screen to that cell's color
 */

void RefreshNoSelect(x,y,w,h,Grid)
short x,y,w,h;
UBYTE Grid[];
{
   register short X,Y;
   register short i;

   for (Y=y; Y<y+h; Y++)
   {
      for (X=x, i=GridX+X+MAXGRIDW*(GridY+Y); X<x+w; X++,i++)
         if (Grid[i] != BLANK) ColorScreenCell(X,Y,Grid[i]);
   }
}


/*
 *  RefreshPatch()
 *
 *  Call the correct refresh routine on the selected patch
 */

void RefreshPatch(x,y,w,h,Grid)
short x,y,w,h;
UBYTE Grid[];
{
   if (SelectType)
      RefreshWithSelect(x,y,w,h,Grid);
     else
      RefreshNoSelect(x,y,w,h,Grid);
}


/*
 *  RefreshScreen()
 *
 *  Refresh the entire visible grid area
 */

void RefreshScreen(Grid)
UBYTE Grid[];
{
   RefreshPatch(0,0,GridW,GridH,Grid);
   UpdateScreen();
}


/*
 *  RefreshSelect()
 *
 *  For each cell in the visible area
 *    If the cell is selected
 *      set the pen color appropriately
 *      set the color of the cell on the screen
 *      if we are in select mode and the cell is selected and we are not moving
 *        or copying, box the selected cell
 *  refresh the screen from the double buffer
 */

void RefreshSelect(Grid)
UBYTE Grid[];
{
   register short X,Y;
   register short i;
   register int Pen;
   int MoveCopy = (MouseMoveType == MM_MOVE || MouseMoveType == MM_COPY);
   int SelectPen = (SelectType == SEL_WAIT)? SELECTPEN: MOVEPEN;

   for (Y=0; Y<GridH; Y++)
   {
      for (X=0, i=GridX+X+MAXGRIDW*(GridY+Y); X<GridW; X++,i++)
      {
         if (CellSelected(i))
         {
            if (MoveCopy) Pen = SelectPen; else Pen = Grid[i];
            ColorScreenCell(X,Y,Pen);
            if (SelectType && NewGen[i] && MoveCopy == FALSE)
               BoxCell(rp,X,Y,MOVEPEN);
         }
      }
   }
   UpdateScreen();
}


/*
 *  Macros for dealing with colors, and the default color table
 */

#define BADVAL(c)       ((c)<0||(c)>15)
#define COLOR(r,g,b)    (((r)<<8)|((g)<<4)|(b))
#define RED(c)          (((c)>>8)&0x0F)
#define GREEN(c)        (((c)>>4)&0x0F)
#define BLUE(c)         ((c)&0x0F)
static UWORD Color[] =
{
   COLOR( 4, 6, 5),         /* background color */
   COLOR(14,15,12),         /* highlight color */
   COLOR( 8,10, 8),         /* foreground color */
   COLOR( 0, 3, 2),         /* shadow color */
   COLOR(15,12, 9),         /* TAIL color */
   COLOR(15, 9, 0),         /* HEAD color */
   COLOR( 9, 6, 1),         /* inverted button color */
   COLOR( 0,10, 0),         /* WIRE color */
};
static UWORD PointerColor;


/*
 *  COlorVal()
 *
 *  Convert a hexadecimal character into an integer value
 */

static short ColorVal(c)
char c;
{
   short color;

   if (c >= '0' && c <= '9')
      color = c - '0';
   else if (c >= 'A' && c <= 'F')
      color = c - 'A' + 10;
   else if (c >= 'a' && c <= 'f')
      color = c - 'a' + 10;
   else color = 16;       /* an illegal value */
   return(color);
}


/*
 *  ReadColorFile()
 *
 *  Open the color file, if it exists
 *    while there are more colors to read, and more lines in the file
 *      if we can read the next line in the file
 *        if there is something on the line to be considered
 *          if command is '=' (use workbench color) indicate that
 *          otherwise if the command is not '*' (use default color)
 *            get the RGB values from the line
 *            give an error if any is no good, and record them if all OK
 *          go on to the next color
 *      otherwise
 *        warn about problems reading the file
 *    if there are additional lines to read after all the colors are
 *      read in, indicate that they are being ignored
 *    close the file
 */

void ReadColorFile()
{
   FILE *InFile;
   char Line[MAXLINE];
   short i = 0;
   short R,G,B;
   
   InFile = fopen(COLORFILE,"r");
   if (InFile)
   {
      while (i<(1<<SCREEND) && !feof(InFile))
      {
         if (fgets(Line,MAXLINE-1,InFile))
         {
            if (Line[0] && Line[0] != ' ' && Line[0] != '\n')
            {
               if (Line[0] == '=')
               {
                  Color[i] = WBCOLOR;
               } else if (Line[0] != '*') {
                  R = ColorVal(Line[0]);
                  G = ColorVal(Line[1]);
                  B = ColorVal(Line[2]);
                  if (strlen(Line) < 4 || BADVAL(R) || BADVAL(G) || BADVAL(B))
                     printf("Invalid Color:  %s",Line);
                    else
                     Color[i] = COLOR(R,G,B);
               }
               i++;
            }
         } else {
            printf("Unexpected End of Color File\n");
         }
      }
      if (fgets(Line,MAXLINE,InFile))
         printf("Extra Lines in Color File Ignored\n");
      fclose(InFile);
   }
}


/*
 *  LoadColors()
 *
 *  For every color in the table that was supposed to come from the workbench
 *    set the color table to that color
 *  Set the screen's color table
 *  Read the preferences setting for the pointer's standard color
 */

void LoadColors()
{
   struct Preferences thePreferences;
   short i;
   short Depth = 1 << myScreen->BitMap.Depth;

   for (i=0; i<Depth; i++)
      if (Color[i] == WBCOLOR)
          Color[i] = GetRGB4(myScreen->ViewPort.ColorMap,i);
   LoadRGB4(&(myScreen->ViewPort),&Color[0],Depth);
   GetPrefs(&thePreferences,sizeof(struct Preferences));
   PointerColor = thePreferences.color18;
}


/*
 *  SetPointerColor()
 *
 *  Set the screen's pointer color to the indicated color values for the
 *  specified color in the color table
 */

void SetPointerColor(i)
short i;
{
   SetRGB4(&(myScreen->ViewPort),POINTERPEN,
      RED(Color[i]),GREEN(Color[i]),BLUE(Color[i]));
}


/*
 *  RestorePointerColor()
 *
 *  Set the pointer color to the default preferences color
 */

void RestorePointerColor()
{
   SetRGB4(&(myScreen->ViewPort),POINTERPEN,
      RED(PointerColor),GREEN(PointerColor),BLUE(PointerColor));
}
