/*
 *  WICONIFY    A utility that allows you to iconify any Intuition window
 *              on any screen, and to open WB windows on any screen.
 *
 *  wFile.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 "wFile.h"

USHORT ImageData[MAXWORDS][MAXHEIGHT][MAXDEPTH];    /* Images are read here */
short MaxWidth,MaxHeight,MaxWords;                  /* Current size of image */
UBYTE Plane0,Plane1;                                /* Bitplanes used */
int ImageType;                                      /* Image, Select, or Mask */
int IconFileOpen;                                   /* TRUE when file is open */

static int ScreenCount;             /* How many screens to ignore */
static short ColorID;               /* Which screen color is next */
static short BitMapLine;            /* Which image line is next */
static int NoMenus;                 /* TRUE if no menus defined */

extern char **ComName;              /* Current command table */
extern int AutoResize;              /* TRUE if windows are sized to fit */
extern int IconifyPriority,         /* Process priority of handler */
           HandlerPriority;         /* Priority in Input Handler chain */


/*
 *  ClearBitMap()
 *
 *   Set each word to zero, and clear the dimensions, bitplane counters
 *   and line count.
 */

static void ClearBitMap()
{
    short i,j,k;
    
    for (i=0; i<MAXWORDS; i++)
       for (j=0; j<MAXHEIGHT; j++)
          for (k=0; k<MAXDEPTH; k++)
             ImageData[i][j][k] = 0;
    MaxWidth = MaxHeight = MaxWords = 0;
    Plane0 = 0xFF; Plane1 = 0x00;
    BitMapLine = 0;
}


/*
 *  SetImage()
 *
 *  Set the PlanePick and PlaneOnOff values according to which planes have
 *  zeros and which have ones (if a plane is all zero or all one it can 
 *  use PlaneOnOff and be excluded from PlanePick).
 *
 *  Get a new Image structure (report an error if out of memory)
 *  Count the number of planes of data (the number of 1's in PlanePick).
 *  Set the Image fields.
 *  If there are any planes of data
 *    Allocate enough CHIP memory for them,
 *    If successful,
 *      Set the ImageData pointer,
 *      For each plane in the image,
 *        if the plane is one of the picked planes,
 *          copy the values of the image data for that plane
 *    Otherwise, report a memory error, and free the image structure
 */

static void SetImage(theImage)
struct Image **theImage;
{
   short i,j,k;
   UBYTE PlanePick = (~Plane0 & Plane1) | (Plane0 & ~Plane1);
   UBYTE PlaneOnOff = Plane0 & Plane1;
   short Depth = 0;
   UWORD *theWord;
   
   if (NEWSTRUCT(Image,*theImage))
   {
      for (k=PlanePick; k; k>>=1) if (k & 1) Depth++;
      (*theImage)->PlanePick  = PlanePick;
      (*theImage)->PlaneOnOff = PlaneOnOff;
      (*theImage)->Width      = MaxWidth;
      (*theImage)->Height     = MaxHeight;
      (*theImage)->Depth      = Depth;

      if (Depth)
      {
         theWord = AllocRaster(MaxWidth,MaxHeight*Depth);
         if (theWord)
         {
            (*theImage)->ImageData = theWord;
            for (k=0; k<MAXDEPTH; k++)
            {
               if (PlanePick & (1<<k))
               {
                  for (j=0; j<MaxHeight; j++)
                     for (i=0; i<MaxWords; i++)
                        *theWord++ = ImageData[i][j][k];
               }
            }
         } else {
            ShowError("Can't get CHIP memory for Image");
            FREESTRUCT(Image,*theImage);
            *theImage = NULL;
         }
      }
   } else ShowError("Can't get memory for Image structure");
}


/*
 *  StartCommand()
 *
 *  Set up anything needed to begin the specified command.  Usually this
 *  means clear any previous value of the appropriate variables or flags,
 *  and possibly recording the command type for future reference.
 */

static void StartCommand(theCommand)
int theCommand;
{
   switch(theCommand)
   {
      case COM_ICONKEY:
         IconifyKey = 0;
         IconifyQuals = IconifyDisquals = 0;
         break;

      case COM_CHANGEREFRESH:
         IconifyChange = 0;
         break;

      case COM_ACTIVEKEY:
         ActivateKey = 0;
         ActivateQuals = 0;
         break;

      case COM_COLORMAP:
         ColorID = 0;
         break;

      case COM_IGNORESCREEN:
         ScreenCount = 0;
         break;

      case COM_DEFAULTFLAGS:
         DefaultFlags = 0;
         break;

      case COM_SCREENFLAGS:
         DefaultScreenFlags = 0;
         break;

      case COM_MENUKEYS:
         NoMenus = TRUE;
         break;

      case COM_DEFAULTIMAGE:
      case COM_DEFAULTSELECT:
      case COM_DEFAULTMASK:
      case COM_SCREENIMAGE:
      case COM_SCREENSELECT:
      case COM_SCREENMASK:
         ClearBitMap();
         ImageType = theCommand;
         break;
   }
}


/*
 *  ContinueCommand()
 *
 *  Do the right thing for each kind of command:
 *
 *  ICONKEY:
 *      Read the next part of a key definition allowing qualifiers, 
 *      disqualifiers, and a key name.  If a key name was given, then
 *      it must be the last thing on the line, and the command is over.
 *
 *  CHANGEREFRESH:
 *      Read the next part of a key definition allowing only qualifiers
 *      Keep reading until the next command is read.
 *
 *  ACTIVEKEY:
 *      Read the next part of a key definition allowing qualifiers or a
 *      key name.  If a key name, it must be the last thing on the line,
 *      and the command is complete.
 *
 *  COLORMAP:
 *      Read the next color in the map.
 *
 *  DEFAULTFLAGS:
 *  SCREENFLAGS:
 *      Read the next flag.  Keep reading until the next command is read.
 *
 *  MENUKEYS:
 *      Read the next menu key equivalent, and indicate that menus have
 *      been processed.
 *
 *  IGNORESCREEN:
 *      Get the title of the screen to ignore, and count the screen.
 *      Read additional screen until the next command is read.
 *
 *  PRIORITY:
 *      Get the process priority and check that it is a legal value.
 *      End the command.
 *
 *  HIRESCLICOM:
 *      Get the CLI command string, and end the command.
 *
 *  LORESCLI:
 *      Get the CLI command string, and end the command.
 *
 *  COMMANDSTACK:
 *      Read the stack size for the CLI command, and check that it is
 *      not too small (report an error if it is, and set it to the minimum).
 *      End the command.
 *
 *  IMAGE, SELECT, or MASK:
 *      Read the next line of the image.
 *
 *  DEFAULTICON:
 *  SCREENICON:
 *      Open an Icon file for processing, and end the command (the next
 *      command will come from the file).
 *
 *  OPENON:
 *      Get the OpenOn menu type and end the command.
 *
 *  AUTORESIZE:
 *      Get the resize flag and end the command.
 *
 *  HANDLERPRI:
 *      Get the Input Device handler priority and check that it is valid.
 *      End the command.
 *
 *  If an end-of-line is required but not seen, give an error and skip
 *    to the beginning og the next line.
 *  Return the type of command that we expect next.
 */

static int ContinueCommand(theCommand)
int theCommand;
{
   int EndOfLine = TRUE;

   switch(theCommand)
   {
      case COM_ICONKEY:
         EndOfLine = ReadKeyDef(&IconifyKey,&IconifyQuals,&IconifyDisquals);
         if (EndOfLine) theCommand = COM_NONE;
         break;

      case COM_CHANGEREFRESH:
         ReadKeyDef(NULL,&IconifyChange,NULL);
         EndOfLine = FALSE;
         break;

      case COM_ACTIVEKEY:
         EndOfLine = ReadKeyDef(&ActivateKey,&ActivateQuals,NULL);
         if (EndOfLine) theCommand = COM_NONE;
         break;

      case COM_COLORMAP:
         ReadColor(ColorID++);
         break;
         
      case COM_DEFAULTFLAGS:
         ReadIconFlags(&DefaultFlags);
         EndOfLine = FALSE;
         break;

      case COM_SCREENFLAGS:
         ReadIconFlags(&DefaultScreenFlags);
         EndOfLine = FALSE;
         break;

      case COM_MENUKEYS:
         ReadMenuKey(); NoMenus = FALSE;
         break;

      case COM_IGNORESCREEN:
         ReadIgnoreScreen();
         ScreenCount++;
         break;

      case COM_PRIORITY:
         ReadInteger(&IconifyPriority);
         if (IconifyPriority < -128 || IconifyPriority > 127)
            ShowError("Priority must be in the range -128 to 127"),
            IconifyPriority = 0;
         theCommand = COM_NONE;
         break;
         
      case COM_HIRESCLICOM:
         ReadString(&HiResCLICommand);
         theCommand = COM_NONE;
         break;

      case COM_LORESCLICOM:
         ReadString(&LoResCLICommand);
         theCommand = COM_NONE;
         break;

      case COM_COMMANDSTACK:
         ReadInteger(&StackSize);
         if (StackSize < 2000)
            ShowError("Stack size must be at least 2K");
            StackSize = 2*1024;
         theCommand = COM_NONE;
         break;
         
      case COM_DEFAULTIMAGE:
      case COM_DEFAULTSELECT:
      case COM_DEFAULTMASK:
      case COM_SCREENIMAGE:
      case COM_SCREENSELECT:
      case COM_SCREENMASK:
         ReadImageLine(BitMapLine++);
         break;

      case COM_DEAFULTICON:
         ReadIconFile(COM_DEFAULTIMAGE);
         theCommand = COM_NONE;
         EndOfLine = FALSE;
         break;

      case COM_SCREENICON:
         ReadIconFile(COM_SCREENIMAGE);
         theCommand = COM_NONE;
         EndOfLine = FALSE;
         break;

      case COM_OPENON:
         ReadOpenOn();
         theCommand = COM_NONE;
         break;

      case COM_AUTORESIZE:
         ReadBool(&AutoResize);
         theCommand = COM_NONE;
         break;

      case COM_HANDLERPRI:
         ReadInteger(&HandlerPriority);
         if (HandlerPriority < 51 || HandlerPriority > 127)
            ShowError("Priority must be in the range 51 to 127"),
            HandlerPriority = 51;
         theCommand = COM_NONE;
         break;
   }
   if (EndOfLine && NextChar != '\n')
   {
      ReadNextWord();
      ShowError("'%s' seen where End-of-Line was expected",Word);
      SkipLine();
   }
   return(theCommand);
}


/*
 *  EndCommand()
 *
 *  Finish the command that is in progress, and check that all parameters,
 *  etc, have been included.  If not, give a message about what was
 *  expected to be seen.  For the image commands, copy the image to the
 *  correct image pointer.
 */
 
static void EndCommand(theCommand,newCommand)
int theCommand,newCommand;
{
   char *Wanted = NULL;

   switch(theCommand)
   {
      case COM_ICONKEY:
         if (IconifyKey == 0) Wanted = "a KEY or BUTTON definition";
         break;

      case COM_ACTIVEKEY:
         if (ActivateKey == 0) Wanted = "a KEY or BUTTON definition";
         break;

      case COM_COLORMAP:
         if (ColorID == 0) Wanted = "an RGB Color";
         break;

      case COM_MENUKEYS:
         if (NoMenus) Wanted = "a Menu Item Name";
         break;

      case COM_IGNORESCREEN:
         if (ScreenCount == 0) Wanted = "a Screen Title";
         break;

      case COM_PRIORITY:
      case COM_COMMANDSTACK:
      case COM_HANDLERPRI:
         Wanted = "a Number";
         break;

      case COM_HIRESCLICOM:
      case COM_LORESCLICOM:
         Wanted = "a CLI Command";
         break;

      case COM_DEFAULTIMAGE:
         if (BitMapLine == 0)
            Wanted = "Image Data";
           else
            SetImage(&DefaultImage);
         break;

      case COM_DEFAULTSELECT:
         if (BitMapLine == 0)
            Wanted = "Image Data";
           else
            SetImage(&DefaultSelect);
         break;

      case COM_DEFAULTMASK:
         if (BitMapLine == 0)
            Wanted = "Mask Data";
           else
            SetImage(&DefaultMask);
         break;

      case COM_SCREENIMAGE:
         if (BitMapLine == 0)
            Wanted = "Image Data";
           else
            SetImage(&DefaultScreenImage);
         break;

      case COM_SCREENSELECT:
         if (BitMapLine == 0)
            Wanted = "Image Data";
           else
            SetImage(&DefaultScreenSelect);
         break;

      case COM_SCREENMASK:
         if (BitMapLine == 0)
            Wanted = "Mask Data";
           else
            SetImage(&DefaultScreenMask);
         break;

      case COM_DEAFULTICON:
      case COM_SCREENICON:
         Wanted = "a File Name";
         break;

      case COM_OPENON:
         Wanted = "ACTIVE_SCREEN, CURRENT_WB, or REAL_WB";
         break;

      case COM_AUTORESIZE:
         Wanted = "TRUE or FALSE";
         break;
   }
   if (Wanted)
   {
      if (newCommand > COM_LAST) Expected(Wanted); else
         ShowError("Keyword '%s' seen where %s was expected",
            ComName[newCommand],Wanted);
   }
}


/*
 *  ReadFile()
 *
 *  While there is more data in the file,
 *    Read the next word from the file and interpret it as a command
 *    If the word was a command and there is an old command in progress
 *      End the old command
 *    If the new command is
 *      Not a command:
 *        If there is a command in progress, do it to the current word,
 *        Otherwise give an error and skip to the next line.
 *      Unknown:
 *        Give a message and skip to the next line.
 *      An End-Of-File:
 *        If an icon file is currently being read, close it,
 *        Otherwise set the end-of-file marker to TRUE.
 *      Anything else (i.e., a legitimate command):
 *        Mark the command as the old command, and setup whatever is needed.
 */
 
static void ReadFile(theFile)
APTR theFile;
{
   int PendingCommand = COM_NONE;
   int NewCommand;
   int EOF = FALSE;

   while (!EOF)
   {
      NewCommand = ReadCommand();
      if (NewCommand != COM_NONE && PendingCommand != COM_NONE)
         EndCommand(PendingCommand,NewCommand), PendingCommand = COM_NONE;
      switch(NewCommand)
      {
         case COM_NONE:
            if (PendingCommand != COM_NONE)
               PendingCommand = ContinueCommand(PendingCommand);
              else
               Expected("Command Keyword"),
               SkipLine();
            break;

         case COM_UNKNOWN:
            ShowError("Unrecognized Command '%s'",Word);
            SkipLine();
            break;

         case COM_EOF:
            if (IconFileOpen) EndIconFile();
               else EOF = TRUE;
            break;

         default:
            PendingCommand = NewCommand;
            StartCommand(PendingCommand);
            break;
      }
   }
}


/*
 *  ReadInitFile()
 *
 *  Try to open the primary initialization file,
 *    If unsuccessful, try the secondary file.
 *  If a file was openned, read the file and then close it.
 */
 
void ReadInitFile(Name1,Name2)
char *Name1,*Name2;
{
   if (OpenFile(Name1) == FALSE)
   {
      if (Name2)
         OpenFile(Name2);
        else
         printf("Warning:  Can't open initialization file '%s'\n",Name1);
   }
   if (InFile)
   {
      ReadFile(InFile);
      CloseFile(InFile);
   }
}
