/*
 *  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:  cDoGadget.c      Handles main control panel gadget events
 */


#include "cGadget.h"
 
int Running;                    /* TRUE if the circuit is running */
int QuitInProgress;             /* TRUE if save requester is up during a QUIT */
int SliderActive;               /* TRUE if a slider knob is being dragged */
int Changed;                    /* TRUE if the circuit has been modified */

extern char FileName[];         /* Circuit's current name */


/*
 *  DoStart()
 *
 *  If the circuit is not already running,
 *    cancel any selection and restore the pointer colors
 *    copy the current states for use by RESET
 *    assume that running will change the circuit
 *    run the circuit until done
 *    reset pointer to drawing color
 *  return FALSE if QUIT was pressed during the run
 */

static short DoStart(NotDone)
short NotDone;
{
   if (!Running)
   {
      if (SelectType) CancelSelect();
      RestorePointerColor();
      CopyGrid(CurGen,ResetArray);
      Changed = TRUE;
      SetRunning();
      NotDone = RunCells(NotDone);
      SetPointerColor(PenInUse);
   }
   return(NotDone);
}


/*
 *  DoReset()
 *
 *  If no cells selected:
 *    setup UNDO unless the circuit is running
 *    update the grid from the reset array and mark as changed
 *    if the circuit is running,
 *      set up the new generation array and linked lists
 *
 *    If cells are selected:
 *      cancel the selection
 */

static void DoReset()
{
   switch(SelectType)
   {
      case SEL_NONE:
         if (!Running) CopyGrid(CurGen,UndoArray);
         UpdateGrid(CurGen,ResetArray);
         Changed = TRUE;
         if (Running)
         {
            CopyGrid(CurGen,NewGen);
            SetupLists(CurGen);
         }
         break;

      case SEL_WAIT:
      case SEL_MOVE:
         CancelSelect();
         DoSelect();
         break;
   }
}

/*
 *  DoUndo()
 *
 *  Mark the circuit as modified
 *  If cells are selected:
 *    Undo only those that are selected, but copy the current states into
 *    the UNDO buffer
 *    Cancel the selection
 *  If cells are being moved or copied:
 *    cancel the selection and do then do the default function:
 *  Otherwise:
 *    swap the current states and the UNDO states and update the screen
 */

static void DoUndo()
{
   register short x,y;
   register short i;
   UBYTE tmp;

   Changed = TRUE;
   switch(MouseMoveType)
   {
      case MM_SELECT:
         for (y=0,i=0; y<MAXGRIDH; y++)
         {
            for (x=0; x<MAXGRIDW; x++,i++)
            {
               if (NewGen[i])
               {
                  tmp = CurGen[i];
                  CurGen[i] = UndoArray[i];
                  UndoArray[i] = tmp;
               } else {
                  UndoArray[i] = CurGen[i];
               }
            }
         }
         CancelSelect();
         break;

      case MM_MOVE:
      case MM_COPY:
         CancelSelect();
      default:
         CopyGrid(CurGen,NewGen);
         UpdateGrid(CurGen,UndoArray);
         CopyGrid(NewGen,UndoArray);
         break;
   }
}


/*
 *  DoClear()
 *
 *  Mark the circuit as modified and sate the current states in the UNDO buffer
 *  If no cells selected:
 *    clear the grid and the screen
 *    set the pen color to WIRE
 *  If cells selected,
 *    clear only those cells that are selected
 *    cancel the selection
 */

void DoClear()
{
   Changed = TRUE;
   CopyGrid(CurGen,UndoArray);
   switch(SelectType)
   {
      case SEL_NONE:
         ClearGrid(CurGen);
         ClearScreen(TRUE);
         SetPenColor(ID_WIREC);
         break;

      case SEL_WAIT:
         RemoveGrid(NewGen,CurGen);
         CancelSelect();
         break;
   }
}


/*
 *  DoClip()
 *
 *  Mark the circuit as modified and save the current states in the UNDO buffer
 *  If cells are selected:
 *    clear the grid and put back only the selected cells
 *    refresh the screen and cancel the selection
 *  If no cells are selected:
 *    clear all cells outside the visible area of the screen
 *    (no refresh is necessary since it can't be seen)
 */

static void DoClip()
{
   register short x,y;
   register short i;

   Changed = TRUE;
   CopyGrid(CurGen,UndoArray);
   switch(SelectType)
   {
      case SEL_WAIT:
         ClearGrid(CurGen);
         ReplaceGrid(NewGen,CurGen);
         ClearScreen(FALSE);
         CancelSelect();
         break;
         
      case SEL_NONE:
         for (y=0,i=0; y<MAXGRIDH; y++)
            for (x=0; x<MAXGRIDW; x++,i++)
               if (x < GridX || x >= GridX+GridW ||
                   y < GridY || y >= GridY+GridH)
                       CurGen[i] = BLANK;
         break;
   }
}


/*
 *  DoCenter()
 *
 *  Cancel any selection.
 *  Mark the circuit as modified and copy the grid into the UNDO buffer
 *  Find the active area of the grid and clear the New Generation array
 *  (as work area).  Clear the screen (it will be refrehsed later).
 *
 *  Calculate the offset to the new center position, and the offset in
 *  the grid array that corresponds to it.  Copy the active area of the
 *  circuit into the New Generation array.
 *
 *  Set the viewing position to the new center.
 *  Clear the grid and update it from the new generation.
 *  Update the slider positions.
 */

static void DoCenter()
{
   short x,y,w,h;
   register short X,Y;
   short dx,dy;
   register short i,di;

   if (SelectType) CancelSelect();

   Changed = TRUE;
   CopyGrid(CurGen,UndoArray);
   GetBounds(CurGen,&x,&y,&w,&h,BLANK);
   ClearGrid(NewGen);
   ClearScreen(FALSE);

   dx = (MAXGRIDW - w) / 2 - x;
   dy = (MAXGRIDH - h) / 2 - y;
   di = dx + MAXGRIDW * dy;
   for (Y=y; Y<y+h; Y++)
      for (X=x,i=X+MAXGRIDW*Y; X<x+w; X++,i++)
         NewGen[i+di] = CurGen[i];

   GridX = (MAXGRIDW - GridW) / 2;
   GridY = (MAXGRIDH - GridH) / 2;
   ClearGrid(CurGen);
   UpdateGrid(CurGen,NewGen);
   UpdateSliders();
}


/*
 *  DoWipe()
 *
 *  Mark the circuit as changed and save the states into the UNDO buffer
 *  If no cells are selected, wipe the electrons off the entire grid,
 *  otherwise wipe the electrons off the new generation grid, and replace
 *  the current states by the (wiped) new generation only where the cells
 *  are selected.  Cancel the selection.
 */

static void DoWipe()
{
   Changed = TRUE;
   CopyGrid(CurGen,UndoArray);
   switch(SelectType)
   {
      case SEL_NONE:
         WipeGrid(CurGen,TRUE);
         break;

      case SEL_WAIT:
         WipeGrid(NewGen,FALSE);
         ReplaceGrid(NewGen,CurGen);
         CancelSelect();
         break;
   }
}


/*
 * DoQuit()
 *
 *  Cancel any selection.
 *  If changes have been made to the circuit since the last SAVE,
 *    Invert the QUIT button and ask if changes should be saved
 *    If yes, then
 *      if we already know the file name, we'll do the save and be done,
 *      otherwise we will put up the requester and need to quit later.
 *      do the save operation
 *    Otherwise ask if they really want to quit
 *    put the QUIT button back to normal
 *  Otherwise
 *    it is safe to quit, so do so.
 */

int DoQuit(NotDone)
int NotDone;
{
   if (SelectType) CancelSelect();
   if (Changed)
   {
      InvertGadget(&cGadget[ID_QUIT]);
      RestorePointerColor();
      if (DoQuestion("Save Changes before Quiting?"))
      {
         if (FileName[0]) NotDone = FALSE; else QuitInProgress = TRUE;
         DoSave();
      } else {
         if (DoQuestion("Really Quit?")) NotDone = FALSE;
      }
      SetPointerColor(PenInUse);
      InvertGadget(&cGadget[ID_QUIT]);
   } else {
      NotDone = FALSE;
   }
   return(NotDone);
}


/*
 *  DoGadgetUp()
 *
 *  Do the right things for each possible button or gadget:
 *
 *  START:      start the circuit
 *  STEP:       cancel the selection and step the circuit
 *  STOP:       stop a running circuit
 *  RESET:      do the Reset function
 *  WIPE:       do the Wipe function
 *  RENAME:     Save the circuit under a different name
 *  LOAD:       Load a circuit or library
 *  NEW:        Clear the grid for a new circuit
 *  SELECT:     Start selecting cells
 *  MOVE:       If cells are selected, move them, otherwise start selecting
 *  COPY:       If cells are selected, copy them, otherwise start selecting
 *  CLEAR:      Do the Clear function
 *  CLIP:       Do the Clip function
 *  PART:       If cells are selected, make them into a part, otherwise
 *              get a part from the parts list
 *  UNDO        Do the Undo function
 *  CENTER:     Do the Center function
 *  SLIDEH:     The horizontal slider is done being dragged
 *  SLIDEV:     The vertical slider is done being dragged
 *  ZOOM:       Zoom in on the grid
 *  SHRINK:     Zoom out from the grid
 *  HELP:       Do the Help function
 *  ABOUT:      Show the About box
 *  QUIT:       Attempt to Quit from CELLS
 *  ARROWS:     Ignore arrow up events
 */

int DoGadgetUp(theGadget,theMessage,NotDone)
struct Gadget *theGadget;
struct IntuiMessage *theMessage;
{
   switch(theGadget->GadgetID)
   {
      case ID_START:
         NotDone = DoStart(NotDone);
         break;

      case ID_STEP:
         if (SelectType) CancelSelect();
         StepCells();
         break;

      case ID_STOP:
         if (Running) SetStopped();
         break;

      case ID_RESET:
         DoReset();
         break;

      case ID_WIPE:
         DoWipe();
         break;

      case ID_SAVE:
         DoSave();
         break;

      case ID_RENAME:
         DoSaveAs();
         break;

      case ID_LOAD:
         DoLoad();
         break;

      case ID_NEW:
         DoNew();
         break;

      case ID_SELECT:
         DoSelect();
         break;

      case ID_MOVE:
         if (SelectType)
            DoMoveCopy(MM_MOVE);
           else
            DoSelect();
         break;

      case ID_COPY:
         if (SelectType)
            DoMoveCopy(MM_COPY);
           else
            DoSelect();
         break;

      case ID_CLEAR:
         DoClear();
         break;

      case ID_CLIP:
         DoClip();
         break;

      case ID_PART:
         if (SelectType)
            AddPart();
           else
            GetPart();
         break;

      case ID_UNDO:
         DoUndo();
         break;

      case ID_CENTER:
         DoCenter();
         break;

      case ID_SLIDEH:
         StopMouseMoves(); SliderActive = FALSE;
         DoSlideH();
         break;

      case ID_SLIDEV:
         StopMouseMoves(); SliderActive = FALSE;
         DoSlideV();
         break;

      case ID_ZOOM:
         DoZoom();
         break;

      case ID_SHRINK:
         DoShrink();
         break;

      case ID_HELP:
         DoHelp();
         break;

      case ID_ABOUT:
         DoAbout();
         break;

      case ID_QUIT:
         NotDone = DoQuit(NotDone);
         break;

      case ID_LARROW:
      case ID_RARROW:
      case ID_UARROW:
      case ID_DARROW:
         break;
   }
   return(NotDone);
}


/*
 *  DoGadgetDown()
 *
 *  Do the right thing for each gadget down event:
 *
 *  SLIDEH or SLIDEV:   if the knob is hit, start reporting mouse moves and
 *                      set the slider active flag, otherwise wait for a
 *                      gadget up event.
 *  Color Gadgets:      Set the point in use to the selected color.
 *  Arrows:             Scroll the correct direction.
 */

void DoGadgetDown(theGadget,theMessage)
struct Gadget *theGadget;
struct IntuiMessage *theMessage;
{
   switch(theGadget->GadgetID)
   {
      case ID_SLIDEH:
      case ID_SLIDEV:
         if (cSlider[theGadget->GadgetID-ID_SLIDEH+PROP_SLIDEH].Flags & KNOBHIT)
         {
            StartMouseMoves();
            SliderActive = TRUE;
         }
         break;

      case ID_BLANKC:
      case ID_WIREC:
      case ID_TAILC:
      case ID_HEADC:
         SetPenColor(theGadget->GadgetID);
         break;

      case ID_LARROW:
         DoScrollLeft(theGadget);
         break;

      case ID_RARROW:
         DoScrollRight(theGadget);
         break;

      case ID_UARROW:
         DoScrollUp(theGadget);
         break;

      case ID_DARROW:
         DoScrollDown(theGadget);
         break;
   }
}
