/*
 *  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:  cMove.c              Handles MOVE and COPY commands.
 */


#include "cGadget.h"

short OffMC;                    /* Offset in state arrays of new position */
static short OffX,OffY;         /* grid offsets of new cell positions */
static short BaseX,BaseY;       /* original (base) position of offsets */
static short BoundX,BoundY,BoundW,BoundH;

extern ULONG Shifted;


/*
 *  CancelMoveCopy()
 *
 *  Cancel mouse moves, and stop buffering the mouse
 *  Put the MOVE or COPY button back to normal, and cancel any selection
 *  Put the gadgets to their proper shifted positions
 *  Restore the pointer to the drawing color
 *  Refresh the selected cells back to their original values
 *  If we were moving cells,
 *    replace them in their original positions
 *    and redraw them on the screen
 */

void CancelMoveCopy(ID)
int ID;
{
   StopMouseMoves(); MouseMoveType = MM_NONE; BufferMouse = FALSE;
   InvertGadget(&cGadget[ID_MOVE]); SelectType = SEL_NONE;
   if (Shifted) ShowShiftedGadgets(); else ShowNormalGadgets();
   SetPointerColor(PenInUse);
   RefreshSelect(CurGen);
   if (ID == MM_MOVE)
   {
      ReplaceGrid(NewGen,CurGen); OffMC = 0;
      RefreshSelect(CurGen);
   }
}


/*
 *  DoMoveCopy()
 *
 *  If the mode is currently SELECT mode:
 *    put the SELECT button back to normal and invert the MOVE or COPY button
 *    set up the UNDO array
 *    set the mouse move type to the specified type (MOVE or COPY)
 *    clear the offset values
 *    If we are movinf cells, clear the selected cells
 *    refresh the selected cells (using the new mode)
 *    get the bounds for the selected cells
 *
 *  Otherwise:
 *    cancel the current MOVE or COPY
 */

void DoMoveCopy(ID)
int ID;
{
   switch (MouseMoveType)
   {
      case MM_SELECT:
         InvertGadget(&cGadget[ID_SELECT]);
         InvertGadget(&cGadget[ID_MOVE]); 
         CopyGrid(CurGen,UndoArray);
         MouseMoveType = ID;
         OffX = OffY = OffMC = 0; BaseX = BaseY = 0;
         if (ID == MM_MOVE) RemoveGrid(NewGen,CurGen);
         RefreshSelect(CurGen);
         GetBounds(NewGen,&BoundX,&BoundY,&BoundW,&BoundH,0);
         break;

      default:
         CancelSelect();
         break;
   }
}


/*
 *  StartMoveCopy()
 *
 *  If the mouse is within the grid area
 *    If the cell where the mouse was pressed is a selected cell
 *      start buffering moves, and start getting mouse moves
 *      set the select type
 *      record the base postition for the move
 *      refresh the cells in the new mode
 */

static void StartMoveCopy(MouseX,MouseY)
short MouseX,MouseY;
{
   short x,y;

   if (InBoard(MouseX,MouseY,&x,&y))
   {
      if (CellColor(NewGen,x+GridX-OffX,y+GridY-OffY))
      {
         BufferMouse = TRUE;
         StartMouseMoves();
         SelectType = (MouseMoveType == MM_MOVE)? SEL_MOVE: SEL_COPY;
         BaseX = x - OffX; BaseY = y - OffY;
         RefreshSelect(CurGen);
      }
   }
}


/*
 *  FinishMoveCopy()
 *
 *  Stop getting mouse moves and stop buffering
 *  go back to waiting for somthing to happen
 *  refresh the selected cells in the new mode
 */

static void FinishMoveCopy(MouseX,MouseY)
short MouseX,MouseY;
{
   StopMouseMoves(); BufferMouse = FALSE;
   SelectType = SEL_WAIT;
   RefreshSelect(CurGen);
}


/*
 *  CellSelected()
 *
 *  offset the cell index to the selected cell's current location
 *  If the index is within the array, then return the cell state
 *  otherwise return 0
 */

int CellSelected(i)
short i;
{
   register int Selected;

   i += OffMC;
   if (i >= 0 && i < ARRAYSIZE) Selected = NewGen[i]; else Selected = FALSE;
   return(Selected);
}


/*
 *  CopySelect()
 *
 *  Record that changes have been made
 *  For each cell in the selection array,
 *    if the index is valid and the cell is selected (i.e., being moved or
 *    copied), then copy the value of the cell into the current grid
 */

static void CopySelect()
{
   register short i,j;
   
   Changed = TRUE;
   for (i=0,j=OffMC; i<ARRAYSIZE && j<ARRAYSIZE; i++,j++)
      if (j >= 0 && NewGen[j]) CurGen[i] = NewGen[j];
}


/*
 *  CalcBounds()
 *
 *  Compute the destination cell positions of the bounding box of the
 *  selected cells, within the screen grid area.  Clip the boundary to 
 *  the active grid.
 */

static void CalcBounds(NewOffX,NewOffY,x,y,w,h)
short NewOffX,NewOffY;
short *x,*y,*w,*h;
{
   if (NewOffX < OffX)
   {
      *x = BoundX + NewOffX;
      *w = BoundX + BoundW + OffX;
   } else {
      *x = BoundX + OffX;
      *w = BoundX + BoundW + NewOffX;
   }
   if (NewOffY < OffY)
   {
      *y = BoundY + NewOffY;
      *h = BoundY + BoundH + OffY;
   } else {
      *y = BoundY + OffY;
      *h = BoundY + BoundH + NewOffY;
   }

   if (*x < GridX) *x = GridX;
   if (*y < GridY) *y = GridY;
   if (*w > GridX + GridW) *w = GridX + GridW;
   if (*h > GridY + GridH) *h = GridY + GridH;
}


/*
 *  MoveSelect()
 *
 *  Calculate the new bounding box positions and size
 *  Calculate the new index offset into the state array
 *  For each cell in the bounded part of the grid
 *    Get the state of the cell that used to be at the current position
 *    and the state of the cell now at the current position
 *    If the cell was selected before but is not any longer,
 *      replace the original cell color
 *    otherwise if the cell was not selected before but is now,
 *      color it the selection color
 *  Update the screen (for double buffered node)
 */

static void MoveSelect(NewOffX,NewOffY)
short NewOffX,NewOffY;
{
   short x,y,w,h;
   short NewOffMC;
   register short X,Y;
   register short i;
   register int Pen1,Pen2;
   int SelectPen = (SelectType == SEL_WAIT)? SELECTPEN: MOVEPEN;
  
   CalcBounds(NewOffX,NewOffY,&x,&y,&w,&h);
   NewOffMC = -(NewOffX + MAXGRIDW * NewOffY);

   for (Y=y; Y<h; Y++)
   {
      for (X=x,i=X+MAXGRIDW*Y; X<w; X++,i++)
      {
         if (i+OffMC >= 0 && i+OffMC < ARRAYSIZE)
            Pen1 = NewGen[i+OffMC]; else Pen1 = 0;
         if (i+NewOffMC >= 0 && i+NewOffMC < ARRAYSIZE)
            Pen2 = NewGen[i+NewOffMC]; else Pen2 = 0;
         if (Pen1 && Pen2 == 0)
            ColorScreenCell(X-GridX,Y-GridY,CurGen[i]);
         else if (Pen1 == 0 && Pen2)
            ColorScreenCell(X-GridX,Y-GridY,SelectPen);
      }
   }
   UpdateScreen();
}


/*
 *  DoCopyMove()
 *
 *  Find the closest board position to the current mouse position
 *  Get the offsets from the base position
 *  If the offsets have changed,
 *    move the selection to the new location
 *    change the offset values
 */

void DoCopyMove(MouseX,MouseY)
short MouseX,MouseY;
{
   short dx,dy;
   
   RestrictToBoard(MouseX,MouseY,&dx,&dy);
   dx -= BaseX; dy -= BaseY;
   if (dx != OffX || dy != OffY)
   {
      MoveSelect(dx,dy);
      OffX = dx; OffY = dy;
      OffMC = -(dx + MAXGRIDW * dy);
   }
}


/*
 *  DoCopyButton()
 *
 *  Do the right thing for each type of button event:
 *
 *  SELECTDOWN:
 *    If we are not already moving or copying, start moving
 *
 *  SELECTUP:
 *    Stop moving for now
 *
 *  MENUDOWN:
 *    Place a copy of the selection at the current location
 *
 *  MENUUP:
 *    If the left button is not down then we are done moving, so
 *      cancel the move or copy (without replacing the cells)
 */

void DoCopyButton(Code,MouseX,MouseY,theMessage)
USHORT Code;
short MouseX,MouseY;
struct IntuiMessage *theMessage;
{
   switch(Code)
   {
      case SELECTDOWN:
         if (SelectType == SEL_WAIT) StartMoveCopy(MouseX,MouseY);
         break;

      case SELECTUP:
         FinishMoveCopy(MouseX,MouseY);
         break;

      case MENUDOWN:
         CopySelect();
         break;
         
      case MENUUP:
         if ((theMessage->Qualifier & IEQUALIFIER_LEFTBUTTON) == 0)
            CancelMoveCopy(MM_COPY);
         break;
   }
}
