/*
 *  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:  cParts.c     Handles creation and maintenance of Parts and Libraries.
 */


#include "cGadget.h"
#include "cRequest.h"
#include "cParts.h"

LIBRARY PartsList = {NULL,NULL, "Parts List",0, PL_STATIC|PL_NODELETE, NULL};
LIBRARY *FirstLibrary = &PartsList;
LIBRARY *SelectedLibrary = &PartsList;


/*
 *  FreePart()
 *
 *  If a part was specified,
 *    If the part has a data section, free it
 *    If it has a name string, free it
 *    If the part is not static, free the part
 */

void FreePart(thePart)
PART *thePart;
{
   if (thePart)
   {
      if (thePart->Data) FREEBLOCK(UBYTE,thePart->w*thePart->h,thePart->Data);
      if (thePart->NameLen) FREECHAR(thePart->Name,thePart->NameLen);
      if ((thePart->Flags & PT_STATIC) == 0) FREESTRUCT(Part,thePart);
   }
}


/*
 *  FreePartList()
 *
 *  Free every part in the specifie part list, and clear the list pointer
 *  to NULL
 */

void FreePartList(thePart)
PART **thePart;
{
   PART *tmpPart;

   while (*thePart)
   {
      tmpPart = *thePart;
      *thePart = (*thePart)->Next;
      FreePart(tmpPart);
   }
}


/*
 *  FreeLibrary()
 *
 *  If a library is specified,
 *    Free any parts in its list
 *    If the library has a name, free it
 *    If the library is no static, free it, otherwise, unlink it
 */

void FreeLibrary(theLibrary)
LIBRARY *theLibrary;
{
   if (theLibrary)
   {
      FreePartList(&(theLibrary->FirstPart));
      if (theLibrary->NameLen) FREECHAR(theLibrary->Name,theLibrary->NameLen);
      if ((theLibrary->Flags & PL_STATIC) == 0)
         FREESTRUCT(Library,theLibrary);
        else
         theLibrary->Next = theLibrary->Prev = NULL;
   }
}


/*
 *  FreeLibList()
 *
 *  For each library in the list, free the library and go on to the next one
 *  Set the library pointer to NULL.
 */

void FreeLibList(theLibrary)
LIBRARY **theLibrary;
{
   LIBRARY *tmpLibrary;
   
   while (*theLibrary)
   {
      tmpLibrary = *theLibrary;
      *theLibrary = (*theLibrary)->Next;
      FreeLibrary(tmpLibrary);
   }
}


/*
 *  ClearLibs()
 *
 *  Clear contents of all libraries, and reset the lists to the standard
 *  Parts List.
 */

void ClearLibs()
{
   FreeLibList(&FirstLibrary);
   FirstLibrary = &PartsList;
   SelectedLibrary = &PartsList;
}


/*
 *  PartToGrid()
 *
 *  Get the size of the part's data area (clipped to the grid size)
 *  Clear the grid to zeros and unpack the data area into the grid.
 *  (a more sophisticated algorithm could be used here - only the first
 *  two bits of each byte are used.  Packing the bits or byte-run compression
 *  would help).
 */

void PartToGrid(Grid,thePart)
UBYTE Grid[];
PART *thePart;
{
   register short X,Y;
   register short i;
   register UBYTE *theByte = thePart->Data;
   short x,y,w,h;
   
   w = thePart->w;
   h = thePart->h;
   x = GridX + (GridW - w) / 2;
   y = GridY + (GridH - h) / 2;
   if (x+w > MAXGRIDW) x = MAXGRIDW-w;
   if (y+w > MAXGRIDH) y = MAXGRIDH-h;
   if (x < 0) x = 0; if (y < 0) y = 0;
   w += x; h += y;

   ZeroGrid(Grid);
   for (Y=y; Y<h; Y++)
      for (X=x, i=X+MAXGRIDW*Y; X<w; X++,i++)
         Grid[i] = *theByte++;
}


/*
 *  GridToPart()
 *
 *  Copy the part data from the specified grid area.
 */

void GridToPart(Grid,x,y,w,h,thePart)
UBYTE Grid[];
short x,y,w,h;
PART *thePart;
{
   register short X,Y;
   register short i;
   register UBYTE *theByte = thePart->Data;

   w += x; h += y;
   for (Y=y; Y<h; Y++)
      for (X=x, i=X+MAXGRIDW*Y; X<w; X++,i++)
         *theByte++ = Grid[i];
}


/*
 *  NewPart()
 *
 *  If we can allocate a new part structure
 *    Get the length of the part name
 *    If we can get a new text string of that size
 *      Copy the name
 *      If we can get a block big enough for the data area
 *        Set the part's width and height
 *        Copy the grid to the part data area
 *      Otherwise
 *        Free the allocated memory and return NULL
 *    Otherwise
 *      Free the allocated memory and return NULL
 *  Give a message if the part could not be made
 */

PART *NewPart(Name,Grid,x,y,w,h)
char *Name;
UBYTE *Grid;
short x,y,w,h;
{
   PART *thePart;

   if (NEWSTRUCT(Part,thePart))
   {
      thePart->NameLen = strlen(Name);
      if (NEWCHAR(thePart->Name,thePart->NameLen))
      {
         strcpy(thePart->Name,Name);
         if (NEWBLOCK(UBYTE,w*h,thePart->Data))
         {
            thePart->w = w;
            thePart->h = h;
            GridToPart(Grid,x,y,w,h,thePart);
         } else {
            FREECHAR(thePart->Name,thePart->NameLen);
            FREESTRUCT(Part,thePart); thePart = NULL;
         }
      } else {
         FREESTRUCT(Part,thePart); thePart = NULL;
      }
   }
   if (thePart == NULL) DoError("Can't Get Memory for Part");
   return(thePart);
}


/*
 *  MakePart()
 *
 *  Get the size of the selected part
 *  If there are no selected cells, give a message
 *  Otherwise, if there is a name provided
 *    Look for the parts (sorted) position in the Parts List
 *    If its place was found (i.e., not already in the list)
 *      Create a new part structure; if allocated OK
 *        Link the part into the list and mark the change
 *    Otherwise
 *      Warn the user that a part with the given name already exists
 *  Otherwise cancel creating the part
 */

PART *MakePart(Name)
char *Name;
{
   PART *thePart = NULL;
   PART *PrevPart;
   PART **PartPtr;
   short x,y,w,h;

   GetBounds(NewGen,&x,&y,&w,&h,0);
   if (x == MAXGRIDW || y == MAXGRIDH)
   {
      DoError("No Cells Selected for Part");
   } else {
      if (Name && Name[0])
      {
         FindListPos(Name,&(PartsList.FirstPart),&PrevPart,&PartPtr);
         if (PartPtr)
         {
            thePart = NewPart(Name,NewGen,x,y,w,h);
            if (thePart)
            {
               thePart->Prev = PrevPart;
               thePart->Next = *PartPtr;
               if (thePart->Next) thePart->Next->Prev = thePart;
               *PartPtr = thePart;
               Changed = TRUE;
            }
         } else {
            DoError("Part '%s' Already Exists",Name);
         }
      } else thePart = CANCELPART;
   }
   return(thePart);
}


/*
 *  FinishPart()
 *
 *  Get the bounds for the cells read in
 *  If no cells were found give a warning
 *  Otherwise
 *    Find the (sorted) position of the part in the Parts List
 *    If it does not already exist in the list
 *      Create a new part structure; if OK
 *        link the part into the list
 *    Otherwise indicate that the part already exists
 */

void FinishPart(Name,theLibrary)
char *Name;
LIBRARY *theLibrary;
{
   short x,y,w,h;
   PART *thePart;
   PART *PrevPart;
   PART **PartPtr;

   GetBounds(NewGen,&x,&y,&w,&h,0);
   if (x == MAXGRIDW || y == MAXGRIDH)
   {
      ReadError("No Cells for Part '%s' in Library '%s'",Name,theLibrary->Name);
   } else {
      FindListPos(Name,&(theLibrary->FirstPart),&PrevPart,&PartPtr);
      if (PartPtr)
      {
         thePart = NewPart(Name,NewGen,x,y,w,h);
         if (thePart)
         {
            thePart->Prev = PrevPart;
            thePart->Next = *PartPtr;
            if (thePart->Next) thePart->Next->Prev = thePart;
            *PartPtr = thePart;
         }
      } else {
         ReadError("Duplicate Part '%s' in Library '%s'",Name,theLibrary->Name);
      }
   }
}


/*
 *  NewLibrary()
 *
 *  If we can allocate a new library structure
 *    Make a copy of the name for the library, if possible
 *  If any errors occur, warn the user.
 */

LIBRARY *NewLibrary(Name)
char *Name;
{
   LIBRARY *theLibrary;

   if (NEWSTRUCT(PartLibrary,theLibrary))
   {
      theLibrary->NameLen = strlen(Name);
      if (NEWCHAR(theLibrary->Name,theLibrary->NameLen))
      {
         strcpy(theLibrary->Name,Name);
      } else {
         FREESTRUCT(PartLibrary,theLibrary);
         theLibrary = NULL;
      }
   }
   if (theLibrary == NULL) ReadError("Can't Get Memory for Library");
   return(theLibrary);
}
