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

#include "wIconFile.h"

ICONPROGRAM  **ProgramPtr = &FirstProgram;  /* Pointer to current program */
ICONSCREEN   **ScreenPtr;                   /* Pointer to current screen */
ICONWINDOW   **WindowPtr;                   /* Pointer to current window */
ICONWINDOW   **FirstWPtr;                   /* Pointer to first window */
ICONDEFINE   **DefinePtr = &FirstDefine;    /* Pointer to current definition */
ICONWINDOW    *IconPtr;                     /* Pointer to current icon */
struct Image **MaskPtr;                     /* Pointer to mask image */

int NoIcon = TRUE;              /* TRUE until an icon command is given */


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

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
 */

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");
   NoIcon = FALSE;
}



/*
 *  CopyIconData()
 *
 *  If there are windows to add icons to,
 *    Look through the window list until we get the the current one,
 *    For each window, copy the correct type of data to the window
 *      from the current icon (the icon of the current window).
 *      Go on to the next window.
 */

void CopyIconData(type)
{
   ICONWINDOW *theWindow;
   WICON *theIcon = &ICON;

   if (FirstWPtr && FIRSTWINDOW)
   {
      theWindow = FIRSTWINDOW;
      while (theWindow && theWindow != WINDOW)
      {
         switch(type)
         {
            case COM_IMAGE:
               theWindow->Icon.Image = theIcon->Image;
               break;

            case COM_SELECT:
               theWindow->Icon.Select = theIcon->Select;
               break;

            case COM_MASK:
               theWindow->Icon.Mask = theIcon->Mask;
               break;

            case COM_FLAGS:
               theWindow->Icon.Flags = theIcon->Flags;
               break;

            case COM_POSITION:
               theWindow->Icon.x = theIcon->x;
               theWindow->Icon.y = theIcon->y;
               break;

            case COM_NAME:
               theWindow->Icon.Name = theIcon->Name;
               break;
         }
         theWindow = theWindow->Next;
      }
   }
}


/*
 *  CopyDefine()
 *
 *  If there is an icon to copy to,
 *    Clear any images or character strings that were allocated previously
 *    If there are windows to copy to,
 *      For each window in the list, set the window's icon to the defined icon
 *    Otherwise
 *      Set the current icon to the specified icon
 */

void CopyDefine(theDefine)
ICONDEFINE *theDefine;
{
   ICONWINDOW *theWindow;

   if (IconPtr)
   {
      if (IconPtr->Flags & IW_IMAGE)  FreeImage(ICON.Image);
      if (IconPtr->Flags & IW_SELECT) FreeImage(ICON.Select);
      if (IconPtr->Flags & IW_MASK)   FreeImage(IconPtr->Mask);
      if (IconPtr->Flags & IW_NAME)   FREECHAR(ICON.Name);
      IconPtr->Flags &= ~IW_ALLOCED; IconPtr->Mask = NULL;

      if (FirstWPtr && FIRSTWINDOW)
      {
         theWindow = FIRSTWINDOW;
         while (theWindow)
         {
            theWindow->Icon = theDefine->Icon;
            theWindow = theWindow->Next;
         }
      } else {
         ICON = theDefine->Icon;
      }
   }
}


/*
 *  FindDefine()
 *
 *  Look through the list of defined icons for the given name.
 *  Return NULL if not found, or a pointer to the definition if found.
 */

ICONDEFINE *FindDefine(Name)
char *Name;
{
   ICONDEFINE *theDefine = FirstDefine;
   int NotFound = TRUE;

   while (theDefine && NotFound)
   {
      if (stricmp(Name,theDefine->Name) == 0) NotFound = FALSE;
        else theDefine = theDefine->Next;
   }
   return(theDefine);
}


/*
 *  NoIconError()
 *
 *  Give an error message
 *  If there were windows expecting an icon,
 *    Free each window in the list
 *  If there was a screen name given
 *    Free the screen structure and clear the pointers
 *    If there was a program name given
 *      Free the program structure and clear its pointers
 *  If there was a DEFINE expecting an icon,
 *    Free the define structure and clear the pointers
 *  Clear the Icon pointer.
 */

void NoIconError()
{
   ICONWINDOW *nextWindow;

   Expected("an Icon specification");
   if (FirstWPtr && FIRSTWINDOW)
   {
      WindowPtr = FirstWPtr;
      while (FIRSTWINDOW)
      {
          nextWindow = FIRSTWINDOW->Next;
          FreeWindow(FIRSTWINDOW);
          FIRSTWINDOW = nextWindow;
      }
      if (ScreenPtr && SCREEN && SCREEN->Window == NULL)
      {
         FreeScreen(SCREEN); SCREEN = NULL; WindowPtr = FirstWPtr = NULL;
         if (PROGRAM && PROGRAM->Screen == NULL)
            FreeProgram(PROGRAM), PROGRAM = NULL, ScreenPtr = NULL;
      }
   }
   if (DEFINE) FreeDefine(DEFINE), DEFINE = NULL;
   IconPtr = NULL;
}


/*
 *  GetNewProgram()
 *
 *  If a definition was in progress, complete it
 *  If there was a previous PROGRAM command, move along in the list
 *  Get a new program structure if possible (error if not)
 *  Set the screen pointer to the program's screen and set the program name
 *  Clear the window and icon pointers.
 */

void GetNewProgram()
{
   if (DEFINE)  DefinePtr  = &(DEFINE->Next);
   if (PROGRAM) ProgramPtr = &(PROGRAM->Next);
   if (NEWSTRUCT(IconProgram,PROGRAM))
   {
      ScreenPtr = &(PROGRAM->Screen);
      PROGRAM->Name = NAME_ANY;
   } else ShowError("Can't get memory for Program data"), ScreenPtr = NULL;
   FirstWPtr = WindowPtr = NULL;
   IconPtr = NULL; NoIcon = TRUE;
}


/*
 *  GetNewScreen()
 *
 *  If there is no place to put the screen pointer, get a new program
 *  If we have a place for the screen
 *    If there is already a screen in progress, move on to the next one
 *    Get a new screen structure if possible (error if not)
 *    Set the window pointers and the screen's name
 *  Clear the icon pointers
 */

void GetNewScreen()
{
   if (ScreenPtr == NULL) GetNewProgram(); 
   if (ScreenPtr)
   {
      if (SCREEN) ScreenPtr = &(SCREEN->Next);
      if (NEWSTRUCT(IconScreen,SCREEN))
         FirstWPtr = WindowPtr = &(SCREEN->Window),
         SCREEN->Name = NAME_ANY;
        else
         ShowError("Can't get memory for Screen data"),
         FirstWPtr = WindowPtr = NULL;
   }
   IconPtr = NULL; NoIcon = TRUE;
}


/*
 *  GetNewWindow()
 *
 *  If there is no place to put the window, get a new screen
 *  If there is a place for the window
 *    If there is already a window in progress, move on to the next one
 *    If there was an icon for the previous window, reset the beginning
 *      of the window list (mutliple windows can be defined at once)
 *    Get a new window structure, if possible (error if not)
 *      Set the mask pointer and the window's name
 *  Set the icon pointer to the current window and clear the icon flag.
 */

void GetNewWindow()
{
   if (WindowPtr == NULL) GetNewScreen();
   if (WindowPtr)
   {
      if (WINDOW) WindowPtr = &(WINDOW->Next);
      if (NoIcon == FALSE) FirstWPtr = WindowPtr;
      if (NEWSTRUCT(IconWindow,WINDOW))
         MaskPtr = &(WINDOW->Mask),
         WINDOW->Name = NAME_ANY;
        else
         ShowError("Can't get memory for Window & Icon data"),
         MaskPtr = NULL;
   }
   IconPtr = WINDOW; NoIcon = TRUE;
}


/*
 *  GetNewDefine()
 *
 *  If a program is in progress, complete it
 *  If a define is in progress, complete it
 *  Get a new define structure if possible (error if not)
 *  Set the mask pointer for the define,
 *  Set the icon pointer to the define strcuture, and clear the icon flag.
 *  Clear the screen and window pointers.
 */

void GetNewDefine()
{
   if (PROGRAM) ProgramPtr = &(PROGRAM->Next);
   if (DEFINE)  DefinePtr  = &(DEFINE->Next);
   if (NEWSTRUCT(IconDefine,DEFINE)) MaskPtr = &(DEFINE->Mask);
      else ShowError("Can't get memory for Icon Definition"),
           MaskPtr = NULL;
   IconPtr = DEFINE; NoIcon = TRUE;
   ScreenPtr = NULL; WindowPtr = FirstWPtr = NULL;
}


/*
 *  CheckForIcon()
 *
 *  If there is no place for an icon to be put
 *    If there was a PROGRAM command
 *      Check if there was a SCREEN command but no WINDOW command
 *        (ie, there is a place to put a window but no window yet)
 *        if so, then this is a screen icon not a window icon
 *      Get a window structure to hold the icon
 *      If it is a screen icon and we have a place for it, set the window's
 *        name to the special flag for screen icons
 *    If we still don't have any place for an icon, return an error condition
 *  Return "everything OK"
 */

int CheckForIcon()
{
   int ScreenIcon = FALSE;

   if (IconPtr == NULL)
   {
      if (PROGRAM)
      {
         ScreenIcon = (WindowPtr && WINDOW == NULL);
         GetNewWindow();
         if (ScreenIcon && WINDOW) WINDOW->Name = SCREENICON;
      }
      if (IconPtr == NULL) return(TRUE);
   }
   return(FALSE);
}



/*
 *  ProgramCompare()
 *
 *  Compare the names of two programs to find which is first alphabetically.
 *  NAME_ANY is considered to be before anything else, then NAME_NULL, then
 *  standard alphabetical order.
 *    Returns -1 if Prog1 before Prog2
 *             0 if Prog1 equals Prog2
 *             1 if Prog1 after  Prog2
 */

static int ProgramCompare(Prog1,Prog2)
ICONPROGRAM *Prog1,*Prog2;
{
   int result = 0;

   if (Prog1->Name != NAME_ANY)
   {
      if (Prog2->Name != NAME_ANY)
      {
         if (Prog1->Name && Prog2->Name)
            result = stricmp(Prog1->Name,Prog2->Name);
           else
            if (Prog1->Name) result = 1; else if (Prog2->Name) result = -1;
      } else result = 1;
   } else if (Prog2->Name != NAME_ANY) result = -1;
   return(result);
}


/*
 *  SortPrograms()
 *
 *  Sort the program list using ProgramCompare to compare names
 *  If the first program in the list is [ANY], then
 *    Save it as ProgramAny and remove it from the list.
 *  Convert the linked list into a balanced binary tree.
 */

void SortPrograms()
{
   extern ICONPROGRAM *mSort(),*ListToTree();
   extern void PrintList();
   
   FirstProgram = mSort(FirstProgram,ProgramCompare,NULL);
   if (FirstProgram && FirstProgram->Name == NAME_ANY)
   {
      ProgramAny = FirstProgram;
      FirstProgram = FirstProgram->Next;
      if (FirstProgram) FirstProgram->Prev = NULL;
      ProgramAny->Next = NULL;
   }
   FirstProgram = ListToTree(FirstProgram,0);
   #ifdef DEBUG
      PrintList(FirstProgram);
      PrintList(ProgramAny);
   #endif
}


#ifdef DEBUG

/*
 *  PrintList()
 *
 *  For each program in the list,
 *    Print its name
 *    For each of its screens
 *      Print the screen name
 *      For each of the screen's windows
 *        Print the window's name
 */

static void PrintList(theProgram)
ICONPROGRAM *theProgram;
{
   ICONSCREEN *theScreen;
   ICONWINDOW *theWindow;
   #define NAME(p)\
      ((p->Name)?((p->Name == NAME_ANY)? "[Any]":\
                 ((p->Name == SCREENICON)? "[Screen Icon]": p->Name)): "[Null]")
   
   while (theProgram)
   {
      printf(">>  '%s'\n",NAME(theProgram));
      theScreen = theProgram->Screen;
      while (theScreen)
      {
         printf(">>    '%s'\n",NAME(theScreen));
         theWindow = theScreen->Window;
         while (theWindow)
         {
            printf(">>      '%s'\n",NAME(theWindow));
            theWindow = theWindow->Next;
         }
         theScreen = theScreen->Next;
      }
      theProgram = theProgram->Next;
   }
}
#endif
