/*
 *  WICONSETTER     A companion utility to wIconify.  wIconSetter allows
 *                  you to specify custom icons for windows ans screens
 *                  that normally use the default icons.
 *
 *  wIconFileRead.c     Reads the initialization file.
 *
 *  Copyright 1990 by Davide P. Cervone, all rights reserved.
 *  You may use this code, provided this copyright notice is kept intact.
 */

#include "wIconFile.h"

UWORD ImageData[MAXWORDS][MAXHEIGHT][MAXDEPTH]; 
short MaxWidth,MaxHeight,MaxWords;
UBYTE Plane0,Plane1;
int ImageType;
int IconFileOpen;
short BitMapLine;

static int NoMaskError;             /* FALSE if a mask error was reported */
static short ErrorCount;            /* number of error messages so far */

#define MAXERROR    8               /* maximum number of errors in a line */


/*
 *  Allowable HEX codes
 */

static char *ExtHex = "0123456789abcdef)!@#$%^&*(ABCDEF";

/*
 *  The initialization file command keywords
 */

static char *MainCommand[] =
{
   "",
   "Program",               /* Specifies Program name for this icon */
   "Screen",                /* Specifies Screen name for this icon */
   "Window",                /* SPecified window name for this icon */
   "Icon",                  /* Uses a DEFINEd icon, or read icon from file */
   "Image",                 /* Sets icon image */
   "Select",                /* Sets icon select image */
   "Mask",                  /* Sets icon mask */
   "Flags",                 /* Sets the icon's flags */
   "Position",              /* Sets initial position of icon */
   "Name",                  /* Sets icon's name when iconified */
   "Define",                /* Defines an icon to be used more than once */
};
#define MAXMAINCOM  COM_LAST

/*
 *  The commands for within an icon file (ICON: FROM commands)
 */

static char *IconCommand[] =
{
   "Image",
   "Select",
   "Mask",
   "Flags",
   "Position",
   "Name"
};
#define MAXICONCOM  5

char **ComName = &MainCommand[0];       /* The current command set */
static int ComOffset;                   /* Add this to get "real" command # */
static int ComCount = MAXMAINCOM;       /* Size of command array */


/*
 *  Icon Flags for FLAGS command
 */

static struct Flag IconFlag[] =
{
   {"NOICONIFY",    WI_NOICONIFY},      /* Window can not be iconified */
   {"NOSAVEPOS",    WI_NOSAVEPOS},      /* Position not saved when openned */
   {"NOCLOSE",      WI_NOCLOSE},        /* CLOSE menu not available */
   {"NOORGANIZE",   WI_NOORGANIZE},     /* Icon not affected by ORGANIZE */
   {"NOMOVE",       WI_NOMOVE},         /* Icon can not be moved */
   {"LOCKED",       WI_LOCKED},         /* Never change icon X,Y */
   {"NOMULTISELECT",WI_NOMULTISELECT},  /* No more than one icon selectable */
   {"CHANGEREFRESH",WI_CHANGEREFRESH},  /* Automatically change refresh type */
   {"AUTOICONIFY",  WI_AUTOICONIFY},    /* Iconify window as it opens */
};
#define MAXICONFLAG 9


/*
 *  ReadCommand()
 *
 *  If we're at the end of the line, read the next line.
 *  Get the next word on the line.
 *  If we have readed the end of the file, use COM_EOF
 *  Otherwise, if the character following the next word is not ':'
 *    then we have not found a command, so use COM_NONE,
 *  Otherwise,
 *    Look for the command in the current command table, and skip the ':'
 *  Return the number of the command found.
 */

int ReadCommand()
{
   int Command = COM_UNKNOWN;
   short i;

   if (NextChar == '\n' || NextChar == 0) ReadNextLine();
   ReadNextWord();
   if (EndOfFile)       Command = COM_EOF; else
   if (NextChar != ':') Command = COM_NONE;
   else
   {
      for (i=0; i<=ComCount && Command == COM_UNKNOWN; i++)
         if (WORDMATCH(ComName[i])) Command = i + ComOffset;
      SkipChar();
   }
   return(Command);
}


/*
 *  ReadIconFlags()
 *
 *  If the word is '+' or '|', then
 *    If there were no previous flags specified, give an error
 *    The next word is the flag name
 *  Look through the flag names
 *    If one of them matches,
 *      Add the flag to the default flags and end the loop
 *  If no flag found, give an error.
 */
 
void ReadIconFlags(theFlags)
ULONG *theFlags;
{
    short i;   
    
    if (Word[0] == '|' || Word[0] == '+')
    {
       if (*theFlags == 0)
          ShowError("Missing Icon Flag before '%c'",Word[0]);
       ReadNextWord();
    }
    for (i=0; i<MAXICONFLAG; i++)
    {
       if (WORDMATCH(IconFlag[i].Name))
          *theFlags |= IconFlag[i].Flag,
          i = MAXICONFLAG+1;
    }
    if (i == MAXICONFLAG) Expected("an Icon Flag");
}


/*
 *  ReadInteger()
 *
 *  Parse the next word as an integer.
 *  If the word is an integer, return it, otherwise give an error.
 */
 
void ReadInteger(i)
int *i;
{
   ReadNextInteger();
   if (sscanf(Word,"%d",i) != 1) Expected("an Integer value");
}


/*
 *  ReadPosition()
 *
 *  Start reading this data again.
 *  If the word begins with a '(',
 *    Skip it and read the x-position.
 *    If the next char is a comma,
 *      Skip it an read the y-position.
 *      If the next character is a ')'
 *        Skip it and save the position values if they are OK.
 *  Otherwise, show an appropriate error.
 */

void ReadPosition(x,y)
WORD *x,*y;
{
   int X = *x,
       Y = *y;

   Reread();
   if (NextChar == '(')
   {
      ReadNextChar(); ReadInteger(&X);
      if (NextChar == ',')
      {
         ReadNextChar(); ReadInteger(&Y);
         if (NextChar == ')')
         {
            ReadNextChar();
            if (X > 0 && Y > 0) *x = X, *y = Y;
         } else Expected(")"), NextChar = '\n';
      } else Expected(","), NextChar = '\n';
   } else Expected("an (x,y) position"), NextChar = '\n';
}


/*
 *  CopyName()
 *
 *  If we can allocate space for the name, copy it,
 *  Otherwise, show an error.
 */

void CopyName(s,Name)
char **s;
char *Name;
{
   if (NEWCHAR(*s,strlen(Name))) strcpy(*s,Name);
      else ShowError("Can't get memory for character string");
}


/*
 *  ReadName()
 *
 *  Get the rest of the input line
 *  If a name string already exists, free it
 *  If the name is [ANY] or [NULL], set the special values,
 *  Otherwise copy the new name to the name string.
 */

void ReadName(name)
char **name;
{
   ReadFullLine();
   if (*name != NAME_NULL && *name != NAME_ANY) FREECHAR(*name);
   if (WORDMATCH("[ANY]"))  *name = NAME_ANY; else
   if (WORDMATCH("[NULL]")) *name = NAME_NULL; else
      CopyName(name,Word);
}


/*
 *  GetPen()
 *
 *  Look for the given HEX digit in the extended HEX list.
 *  If not found,
 *    Give an error message and count the number of errors.
 *    If more than the maximum allowed, skip the rest of the line.
 *  If the image type is a MASK, and the pen is not 0 or 1,
 *    If there were no previous errors of this type, give one.
 *    Mark that the user was warned about mask pen types.
 *    Reduce the pen number to 0 or 1.
 *  Return the pen color.
 */
 
static UBYTE GetPen(c)
char c;
{
   UBYTE Pen = 0;
   short i;

   for (i=0; i<32; i++) if (c == ExtHex[i]) Pen = i, i = 33;
   if (i == 32)
   {
      Expected("HEX Digit"); ErrorCount++;
      if (ErrorCount > MAXERROR)
         printf("Maximum errors exceeded - skipping rest of line\n"),
         SkipLine();
   }
   if (Pen > 1 && ImageType == COM_MASK)
   {
      if (NoMaskError) ShowError("Mask pixel values must be 0 or 1");
      NoMaskError = FALSE;
      Pen = Pen & 1;
   }
   return(Pen);
}


/*
 *  ReadImageLine()
 *
 *  If the current line is within the maximum image size,
 *    Clear the error flags, and get back to the beginning of the line.
 *    While we're not at the end of the line,
 *      If the current column is within the maximum image size,
 *        Get the pen number of the current pixel, and increment the column.
 *        Update Plane0 and Plane1 (Plane0 has a zero wherever that plane
 *          has at least one pixel that is zero; Plane1 has a 1 wherever
 *          that plane has at least one pixel that is one).
 *        If the bit mask has wrapped around, go on to the next image word.
 *        For each 1 bit in the Pen color, add a 1 bit into the image data
 *        Shift the image mask to the right.
 *        Get the next pixel's pen color and skip any comments.
 *        Skip trailing blanks (but not interior blanks).
 *      Otherwise, indicate that the width is too large and skip to the
 *        end of the line.
 *    Update the current image size, if necessary.
 *  Otherwise, indicate that the icon is too tall, and skip to the next line.
 */
 
void ReadImageLine(y)
short y;
{
   short x = 0, w = 0;
   short d;
   UWORD mask = BIT(15);
   UBYTE Pen;
   
   if (y < MAXHEIGHT)
   {
      NoMaskError = TRUE; ErrorCount = 0;
      Reread();
      while (NextChar != '\n')
      {
         if (x < MAXWIDTH)
         {
            Pen = GetPen(NextChar); x++;
            Plane0 &= Pen; Plane1 |= Pen; d = 0;
            if (mask == 0) w++, mask = BIT(15);
            while (Pen)
            {
               if (Pen & 1) ImageData[w][y][d] |= mask;
               Pen >>= 1; d++;
            }
            mask >>= 1;
            GetNextChar(); SkipComments();
            if (NextChar == ' ')
            {
               SkipSpaces();
               if (NextChar != '\n') Reread();
            }
         } else {
            ReadFullLine();
            ShowError("Maximum Image Width of %d exceeded",MAXWIDTH);
         }
      }
      if (x   > MaxWidth)  MaxWidth = x, MaxWords = w+1;
      if (y+1 > MaxHeight) MaxHeight = y+1;
      if (MaxWidth > MAXWIDTH) MaxWidth = MAXWIDTH;
   } else {
      ReadFullLine();
      ShowError("Maxmimum Image Height of %d exceeded",MAXHEIGHT);
   }
}


/*
 *  ReadIconFile()
 *
 *  Get the rest of the line (it will be the file name to open)
 *  Save the current file information.
 *  Attempt to open the specified file.
 *  If openned OK, then
 *    Set the icon file flag,
 *    Set the command list to the icon file command list and size.
 *  Otherwise,
 *    Put back the old file information,
 *    Give an error about the icon file.
 */
 
void ReadIconFile()
{
   ReadFullLine();
   SaveOpenFile();
   if (OpenFile(Word))
   {
      IconFileOpen = TRUE;
      ComName = &IconCommand[0];
      ComOffset = COM_IMAGE;
      ComCount = MAXICONCOM;
   } else {
      RestoreFile();
      ShowError("Can't open Icon file '%s'",Word);
   }
}


/*
 *  EndIconFile()
 *
 *  Put back the old file information
 *  Mark the file as closed, and restore the command list to the main list.
 */
 
void EndIconFile()
{
   RestoreFile();
   IconFileOpen = FALSE;
   ComName = &MainCommand[0];
   ComOffset = 0;
   ComCount = MAXMAINCOM;
}
