/*  Siteswap v1.2 beta 1
 *  Released on 01/13/02 by Nathan Peterson
 *
 *  siteswap.c contains PalmOS user interface
 *  Originally written by Stuart Macmillan, modified by Nathan Peterson
 *  Based off of Glenn Hutchings Juggle 1.0
 *  Contact info: Nathan Peterson (nathan@juggler.net)
 *  Download the latest version at http://www.siteswap.org/palm/
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 */

/*
** PalmOS includes
*/
#include <PalmTypes.h>
#include <PalmCompatibility.h>
#include <Field.h>
#include <Form.h>
#include <SoundMgr.h>
#include <StringMgr.h>
#include <Menu.h>
#include <Window.h>
#include <Chars.h>
#include <Keyboard.h>

#include "globals.h"
#include "juggle.h"
#include "generate.h"

#define cmdAbout        'a'
#define cmdOptns        'o'
#define cmdSiteSel      's'
#define cmdGenerate     'g'
#define cmdHelp			'h'
#define cmdInpSiteSel   'i'


CharPtr wchItm;
char wchItmString[21];

// initialize global variables
Boolean pref_Sound = false;		/* Sound on or off	*/
Boolean pref_Pause = false;		/* Pause on or off	*/
Boolean pref_Hold = true;		/* Hold balls of height 2 */

// global variables for gss other form
int gss_delay = 0, gss_slot = 1;
int gss_xself[21], gss_iself[21], gss_xpass[21];
int gss_xself_len = 0, gss_iself_len = 0, gss_xpass_len = 0;

// declare popup vars for generate form
int popup_mode, popup_type, popup_state;

//Boolean debug_global=false;
//static FieldPtr sslabel;

int debug_global = 0;

//void SSInputEvent ();
static void UpdateScrollbar(UInt16 scrollID, UInt16 fieldID);
static void ScrollLines(int numLinesToScroll, UInt16 scrollID, UInt16 fieldID, Boolean redraw);
static void PageScroll(WinDirectionType direction, UInt16 scrollID, UInt16 fieldID);
Boolean CheckNums(CharPtr s);


/* SSFLDUpd()
 * updates fields for formTBassesm
 *********************************************/
void SSFLDUpd(CharPtr ss, int balls ){
  FormPtr form;
  FieldPtr fldptr;
  static Char balls_string[3];


  StrIToA(balls_string, balls);

  // set and draw siteswap field
  form = FrmGetFormPtr(formTBasesm);
  fldptr = FrmGetObjectPtr(form,FrmGetObjectIndex(form, FldPttrn));
  FldSetTextPtr(fldptr, ss);
  FldDrawField(fldptr);      // for some reason, only 10 chars get printed out :(

  // set and draw # of balls field
  fldptr = FrmGetObjectPtr(form,FrmGetObjectIndex(form, FldDispBalls));
  FldSetTextPtr(fldptr, balls_string);
  FldDrawField(fldptr);

}


/* refresh_main_form()
 * refreshes mainForm.  Form must be active
 *********************************************/
void refresh_main_form()
{
  FormPtr form;
  RectangleType bounds;

  form = FrmGetActiveForm();

  // Draw form and update field
//  FrmDrawForm(form);
//  sslabel = FrmGetObjectPtr(form,FrmGetObjectIndex(form, FldPttrn));
  SSFLDUpd( wchItm, juggle_nballs );

  // set initial values for push buttons
  FrmSetControlValue(form,FrmGetObjectIndex(form,bID_Sound),pref_Sound);
  FrmSetControlValue(form,FrmGetObjectIndex(form,bID_Draw),pref_Draw);
  FrmSetControlValue(form,FrmGetObjectIndex(form,bID_Pause),pref_Pause);
  FrmSetControlValue(form,FrmGetObjectIndex(form,bID_Hold),pref_Hold);


  // Draw borders around buttons (not push buttons)
  FrmGetObjectBounds(form,FrmGetObjectIndex(form,bID_Objects), &bounds);
  WinDrawRectangleFrame(1,&bounds);
  FrmGetObjectBounds(form,FrmGetObjectIndex(form,bID_Random), &bounds);
  WinDrawRectangleFrame(1,&bounds);
  FrmGetObjectBounds(form,FrmGetObjectIndex(form,bID_SSinput), &bounds);
  WinDrawRectangleFrame(1,&bounds);
  FrmGetObjectBounds(form,FrmGetObjectIndex(form,bID_SSselect), &bounds);
  WinDrawRectangleFrame(1,&bounds);
  FrmGetObjectBounds(form,FrmGetObjectIndex(form,bID_SSgenerate), &bounds);
  WinDrawRectangleFrame(1,&bounds);

}


/* KeyDown()
 * Act on a key (or menu event) received.
 *******************************************/
static Boolean KeyDown(UInt chr)
{
  Boolean handled = false;

  /* Map command character to lower case */
  if (chr >= 'A' && chr <= 'Z')
    chr += 'a'-'A';

  /* Dispatch on character */
  switch (chr) {

  case cmdAbout:
	FrmPopupForm(aboutBasesm);
    handled = true;
    break;

  case cmdSiteSel:
	handled = true;
	FrmPopupForm(formTSelectSS);
	break;

  case cmdInpSiteSel:
	handled = true;
	FrmPopupForm(formTInputSS);
	break;

  case cmdGenerate:
	handled = true;
	FrmPopupForm(formTGSS);
	break;

  case cmdOptns:
	handled = true;
	FrmPopupForm(formTOptions);
	break;

  case cmdHelp:
	FrmPopupForm(helpBasesm);
    handled = true;
    break;

  default: // else do nothing
    break;

  }
  return handled;
}


/* InitialJugEvent()
 * handles the splash screen (not currently used)
 ***************************************************/
static Boolean InitialJugEvent(EventPtr e)
{

  switch (e->eType) {

  case frmOpenEvent:
    FrmDrawForm(FrmGetActiveForm());
    break;

  case ctlSelectEvent:
    if ( e->data.ctlEnter.controlID == bID_Start_w )
       {
		pref_Sound = true;
        FrmGotoForm( formTBasesm );
          wchItm = "5";
          jug_setup(wchItm);
       }
	else if ( e->data.ctlEnter.controlID == bID_Start_wo )
	{
		pref_Sound = false;
        FrmGotoForm( formTBasesm );
          wchItm = "5";
          jug_setup(wchItm);
	}
    break;

  default: // else do nothing
	  break;
  }

  return false;

}


/* HelpEvent()
 * Display the Help text
 ***************************/
static Boolean HelpEvent(EventPtr e)
{
  FormPtr form;

  FieldPtr field;
  Boolean handled = false;
  Char *text; // Help_text

  switch (e->eType) {

	case frmOpenEvent:
	  form = FrmGetActiveForm();
	  FrmDrawForm(form);

	  text = Help_text;
	  field = FrmGetObjectPtr(form,FrmGetObjectIndex(form,HelpField));
	  FldSetTextPtr(field,text);
	  FldRecalculateField(field,true);
	  FldDrawField (field);

	  UpdateScrollbar(HelpScrollBar, HelpField);
	  break;

	case ctlSelectEvent:
	  if ( e->data.ctlEnter.controlID == bID_helpClose ) {
		FrmReturnToForm( formTBasesm );
		handled = true;
	  }
	  break;

	case keyDownEvent:
	  if (e->data.keyDown.chr == pageUpChr) {
		PageScroll(winUp, HelpScrollBar, HelpField);
		handled = true;
	  } else if (e->data.keyDown.chr == pageDownChr) {
		PageScroll(winDown, HelpScrollBar, HelpField);
		handled = true;
	  }
	  break;

	case sclRepeatEvent:
	  ScrollLines(e->data.sclRepeat.newValue - e->data.sclRepeat.value, HelpScrollBar, HelpField, false);
	  break;

	default: // else do nothing
	  break;
  }

  return handled;

}



/* AboutEvent()
 * Display the About text
 ***************************/
static Boolean AboutEvent(EventPtr e)
{
  FormPtr form;

  FieldPtr field;
  Boolean handled = false;
  Char *text;// = About_text;

  switch (e->eType) {

	case frmOpenEvent:
	  form = FrmGetActiveForm();
	  FrmDrawForm(form);

	  text = About_text;
	  field = FrmGetObjectPtr(form,FrmGetObjectIndex(form,AboutField));
	  FldSetTextPtr(field,text);
	  FldRecalculateField(field,true);
	  FldDrawField (field);

	  UpdateScrollbar(AboutScrollBar, AboutField);
	  break;

	case ctlSelectEvent:
	  if ( e->data.ctlEnter.controlID == bID_aboutClose ) {
		FrmReturnToForm( formTBasesm );
		handled = true;
	  }
	  break;

	case keyDownEvent:
	  if (e->data.keyDown.chr == pageUpChr) {
		PageScroll(winUp, AboutScrollBar, AboutField);
		handled = true;
	  } else if (e->data.keyDown.chr == pageDownChr) {
		PageScroll(winDown, AboutScrollBar, AboutField);
		handled = true;
	  }
	  break;

	case sclRepeatEvent:
	  ScrollLines(e->data.sclRepeat.newValue - e->data.sclRepeat.value, AboutScrollBar, AboutField, false);
	  break;

	default: // else do nothing
	  break;
  }

  return handled;

}

// UpdateScrollbar()
static void UpdateScrollbar(UInt16 scrollID, UInt16 fieldID)
{
  FormPtr		frm = FrmGetActiveForm();
  ScrollBarPtr	scroll;
  FieldPtr		field;
  Word			currentPosition, textHeight, fieldHeight, maxValue;

  field = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,fieldID));
  FldGetScrollValues(field, &currentPosition, &textHeight, &fieldHeight);

  if (textHeight > fieldHeight)
	maxValue = textHeight - fieldHeight;
  else if (currentPosition)
	maxValue = currentPosition;
  else
	maxValue = 0;
  scroll = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,scrollID));
  SclSetScrollBar(scroll, currentPosition, 0, maxValue, fieldHeight - 1);
}

// ScrollLines()
static void ScrollLines(int numLinesToScroll, UInt16 scrollID, UInt16 fieldID, Boolean redraw)
{
  FormPtr	frm = FrmGetActiveForm();
  FieldPtr	field;

  field = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, fieldID));
  if (numLinesToScroll < 0)
	FldScrollField(field, -numLinesToScroll, winUp);
  else
	FldScrollField(field, numLinesToScroll, winDown);

  // if there are blank lines at the end and we scroll up, FldScrollField
  // makes the blank lines disappear.  Therefore, we've got to update
  // the scrollbar
  if ((FldGetNumberOfBlankLines(field) && numLinesToScroll <0) || redraw)
	UpdateScrollbar(scrollID, fieldID);
}

// PageScroll()
static void PageScroll(WinDirectionType direction, UInt16 scrollID, UInt16 fieldID)
{
  FormPtr	frm;
  FieldPtr	field;
  int		linesToScroll;

  frm = FrmGetActiveForm();
  field = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, fieldID));

  if (FldScrollable(field, direction)) {
	linesToScroll = FldGetVisibleLines(field) - 1;

	if (direction == winUp)
	  linesToScroll = -linesToScroll;
	ScrollLines(linesToScroll, scrollID, fieldID, true);
  }
}


/* JuggleEvent()
 * Event handler for main form
 ********************************/
static Boolean JuggleEvent(EventPtr e)
{
  FormPtr form;
  Boolean handled = false;
//  RectangleType bounds;

  switch (e->eType) {

  case frmOpenEvent:
	form = FrmGetActiveForm();
    FrmDrawForm(form);

	refresh_main_form();


    handled = true;
    break;

  case appStopEvent:
  case frmCloseEvent:
    handled = true;
    break;

  case menuEvent:
    handled = KeyDown(e->data.menu.itemID-menuBasesm);
    break;

  case nilEvent:
    if ( !pref_Pause) jug_continue();
    handled = true;
    break;



  case ctlSelectEvent:

	switch ( e->data.ctlEnter.controlID ) {

	  case bID_Sound :
		pref_Sound = !pref_Sound;
		handled = true;
		break;

	  case bID_Objects :
		juggle_setdp();
		handled = true;
		break;

	  case bID_Draw :
		pref_Draw = !pref_Draw;
		juggle_frame();
		handled = true;
		break;

	  // can be used for debugging
	  case bID_Print :
		form = FrmGetActiveForm();
		if (FrmGetControlValue(form,FrmGetObjectIndex(form,bID_Print))==true) {
		  pref_Pause = true;
		  print_jug_data();
		}
		else {
		  pref_Pause = FrmGetControlValue(form,FrmGetObjectIndex(form,bID_Pause));
		  juggle_update();
		}
		handled = true;
		break;

      case bID_Pause :
		pref_Pause = !pref_Pause;
		handled = true;
		break;

      case bID_Random :
       jug_random();
       handled = true;
       break;

	  case bID_Hold :
		pref_Hold = !pref_Hold;
		handled = true;
		break;

	  case bID_SSinput :
		handled = true;
		FrmPopupForm(formTInputSS);
		break;

	  case bID_SSselect :
		handled = true;
		FrmPopupForm(formTSelectSS);
		break;

	  case bID_SSgenerate :
		handled = true;
		FrmPopupForm(formTGSS);
		break;

	  default: // else do nothing
		break;

    }
    break;

  default: // else do nothing
	break;
  }
  return handled;
}


/* SSInputEvent()
 * handles the events for the SiteSwap input form
 ***************************************************/
static Boolean SSInputEvent(EventPtr e)
{
  Boolean handled = false;
  FormPtr form;
  UInt16 objIndex;
  static MemHandle text_Handle;
  static   CharPtr text_Pointer;


  switch (e->eType) {

  case frmOpenEvent:
    // allocate memory for text handle (freed automatically, part of form)
    text_Handle = MemHandleNew(21*sizeof(char));

    // initialize siteswap text
    text_Pointer = MemHandleLock(text_Handle);
    StrCopy(text_Pointer, wchItmString);
    MemHandleUnlock(text_Handle);

    // set focus, set text, draw form
    form = FrmGetActiveForm();
    objIndex = FrmGetObjectIndex(form, FldInpSS);
    FrmSetFocus(form, objIndex);
    FldSetTextHandle(FrmGetObjectPtr(form, objIndex), text_Handle);
    FrmDrawForm(form);

    handled = true;
    break;

  case appStopEvent:
  case frmCloseEvent:
    FrmReturnToForm( formTBasesm );
    handled = true;
    break;

  case ctlSelectEvent:

    if ( e->data.ctlEnter.controlID == bID_InpCopy){
		form = FrmGetActiveForm();
		FldCopy(FrmGetObjectPtr(form,FrmGetObjectIndex(form,FldInpSS)));
    }
	else if ( e->data.ctlEnter.controlID == bID_InpPaste){
		form = FrmGetActiveForm();
		FldPaste(FrmGetObjectPtr(form,FrmGetObjectIndex(form,FldInpSS)));
    }
    else if ( e->data.ctlEnter.controlID == bID_InpClose){
	  text_Pointer = MemHandleLock(text_Handle);
      if ( jug_setup( text_Pointer )) {
	    StrCopy(wchItmString, text_Pointer);
	    wchItm = wchItmString;
	  }
      else {
	    FrmAlert(errorBasesm);
	    handled = true;
	    break;
	  }
      MemHandleUnlock(text_Handle);
      FrmReturnToForm( formTBasesm );
      SSFLDUpd(wchItm,juggle_nballs);
      handled = true;
	}
    else if ( e->data.ctlEnter.controlID == bID_InpCancel){
      FrmReturnToForm( formTBasesm );
      handled = true;
	}
	break;

  default: // else do nothing
	break;
  }

  return handled;
}


/* SelectSSEvent()
 * offers a list of siteswaps for the user to choose from
 ***********************************************************/
static Boolean SelectSSEvent(EventPtr e)
{
  Boolean handled = false;
  FormPtr form;
  ListPtr SSSelLst;

  switch (e->eType) {

  case frmOpenEvent:

    form = FrmGetActiveForm();
    FrmDrawForm(form);
    handled = true;
    break;

  case appStopEvent:
  case frmCloseEvent:
    form = FrmGetActiveForm();
    FrmDeleteForm(form);
    handled = true;
    break;

  case lstSelectEvent:
    if ( e->data.ctlEnter.controlID == ListSiteList ){
		form = FrmGetActiveForm();
        SSSelLst = FrmGetObjectPtr( form, FrmGetObjectIndex( form, ListSiteList) );
        wchItm =  LstGetSelectionText(SSSelLst, e->data.lstSelect.selection  );
	}
      handled = true;
      break;

  case ctlSelectEvent:

    if ( e->data.ctlEnter.controlID == bID_SelClose) {
//        jug_clear();

	  if ( jug_setup(wchItm)) {
//		SSFLDUpd(wchItm);
		StrCopy(wchItmString,wchItm);
		wchItm = wchItmString;
	  }
	  else {
		FrmAlert(errorBasesm);
		handled = true;
		break;
	  }

	  FrmReturnToForm( formTBasesm );
	  SSFLDUpd(wchItm,juggle_nballs);
	  //pref_Pause=false;
	  handled = true;

	}
	else if ( e->data.ctlEnter.controlID == bID_SelCancel) {

	  wchItm = wchItmString;
	  FrmReturnToForm( formTBasesm );
	  //pref_Pause=false;
	  handled = true;

	}

	break;

  default: // else do nothing
	break;
  }
  return handled;
}


/* StepInput()
 * prompts the user to enter a step size
 ******************************************/
void StepInput ()
{
  VoidHand nameH;
  CharPtr nameP;
  FormPtr curFormP;
  FormPtr formP;
  Word objIndex;
  Word buttonHit;
  unsigned step;

  /* Allocate a chunk for the user to edit.  The field in the dialog	*/
  /* requires the text to be in a chunk so it can be resized.			*/
  nameH = MemHandleNew(4);
  nameP = MemHandleLock(nameH);

  StrIToA(nameP, (int)(juggle_step*1000));

  MemHandleUnlock(nameH);

  curFormP = FrmGetActiveForm ();
  formP = FrmInitForm (formTInputStep);

  /* Set the field to edit the name. */
  objIndex = FrmGetObjectIndex (formP, FldStepSize);
  FldSetTextHandle(FrmGetObjectPtr (formP, objIndex), (Handle) nameH);

  FrmSetFocus(formP, objIndex); /* Set the insertion point blinking in the only field */


  /* Allow the user to type in a name.  Wait until	*/
  /* a button is pushed (OK is the only button).		*/
  buttonHit = FrmDoDialog (formP);

  /* Take the text handle from the field so the text isn't deleted when the form is. */
  FldSetTextHandle(FrmGetObjectPtr (formP, objIndex), 0);

  FrmDeleteForm (formP);					/* Deletes the field's new text. */
  FrmSetActiveForm (curFormP);

	// if OK pressed, check step size
	if (buttonHit == bID_stepClose )
	{
	  nameP = MemHandleLock(nameH);
	  step = StrAToI(nameP);
	  if ( step < 1000 && step >= 1)
		  juggle_step = ((double)step)/1000;
	  else { FrmAlert(errorStep); }
	}
	// else cancel

	MemHandleFree(nameH);	// The name is now recorded and no longer needed
}


/* OptionsEvent()
 * handles the events for the options form
 ********************************************/
static Boolean OptionsEvent(EventPtr e)
{
  Boolean handled = false;
  FormPtr form;
  static Boolean Sound_old, Hold_old, BackBuff_old, Draw_old;
  static int jugProp_old;
  static double Step_old;

  switch (e->eType) {

  case frmOpenEvent:

    juggle_set_draw_front();
    form = FrmGetActiveForm();
    FrmDrawForm(form);

	// initialize the options objects
	FrmSetControlValue(form,FrmGetObjectIndex(form,cID_o_Sound),pref_Sound);
	FrmSetControlValue(form,FrmGetObjectIndex(form,cID_o_Hold),pref_Hold);
//	FrmSetControlValue(form,FrmGetObjectIndex(form,cID_o_BackBuff),pref_BackBuff);
	FrmSetControlValue(form,FrmGetObjectIndex(form,cID_o_Draw),pref_Draw);
	CtlSetLabel(FrmGetObjectPtr(form,FrmGetObjectIndex(form,pID_objects)),
	  LstGetSelectionText(FrmGetObjectPtr(form,FrmGetObjectIndex(form,lID_objects)),jugProp));

	/* Save old values in case user cancels */
	Sound_old = pref_Sound;
	Hold_old = pref_Hold;
	BackBuff_old = pref_BackBuff;
	Draw_old = pref_Draw;
	jugProp_old = jugProp;
	Step_old = juggle_step;


    handled = true;
    break;

  case appStopEvent:
  case frmCloseEvent:
//	juggle_set_draw_back();
    form = FrmGetActiveForm();
    FrmDeleteForm(form);
    handled = true;
    break;

  case ctlSelectEvent:

    if ( e->data.ctlEnter.controlID == bID_OptClose)
	{
	  /* must restart pattern if step size has changed */
	  if (juggle_step != Step_old)
		jug_setup(wchItm);

	  FrmReturnToForm( formTBasesm );
	  pref_Pause=false;
	  handled = true;
	}
    else if ( e->data.ctlEnter.controlID == bID_OptCancel)
	{

	  FrmReturnToForm( formTBasesm );

	  /* restore previous values */
	  pref_Sound = Sound_old;
	  pref_Hold = Hold_old;
	  pref_BackBuff = BackBuff_old;
	  pref_Draw = Draw_old;
	  jugProp = jugProp_old;
	  juggle_step = Step_old;

	  pref_Pause=false;
	  handled = true;
	}
	else if ( e->data.ctlEnter.controlID == cID_o_Sound )
	{
	  pref_Sound = !pref_Sound;
	  handled = true;
	}
	else if ( e->data.ctlEnter.controlID == cID_o_Hold )
	{
	  pref_Hold = !pref_Hold;
	  handled = true;
	}
//	else if ( e->data.ctlEnter.controlID == cID_o_BackBuff )
//	{
//	  pref_BackBuff = !pref_BackBuff;
//	  handled = true;
//	}
	else if ( e->data.ctlEnter.controlID == cID_o_Draw )
	{
	  pref_Draw = !pref_Draw;
	  handled = true;
	}
	else if ( e->data.ctlEnter.controlID == sID_StepSize )
	{
	  StepInput();
	  handled = true;
	}

	break;

  case popSelectEvent:
	jugProp = e->data.popSelect.selection;
	handled = false;
	break;

  default: // else do nothing
	break;
  }

  return handled;
}


/* GenerateEvent()
 * handles the events for the Generate SiteSwap form
 ***************************************************/
static Boolean GenerateEvent(EventPtr e)
{
  Boolean handled = false;
  FormPtr form;
//  Word objIndex;
  int balls, maxthrow, length, multhrow;
  int flags[10], xlen[3]; // flags to pass to generate_siteswaps()
  MemHandle balls_handle, maxthrow_handle, length_handle, multhrow_handle;
  CharPtr balls_txt, maxthrow_txt, length_txt, multhrow_txt;

  switch (e->eType) {

  case frmOpenEvent:

    // initialize text fields
    balls_handle = MemHandleNew(3*sizeof(char));
    maxthrow_handle = MemHandleNew(3*sizeof(char));
    length_handle = MemHandleNew(3*sizeof(char));
    multhrow_handle = MemHandleNew(3*sizeof(char));
    balls_txt = MemHandleLock(balls_handle);
    maxthrow_txt = MemHandleLock(maxthrow_handle);
    length_txt = MemHandleLock(length_handle);
    multhrow_txt = MemHandleLock(multhrow_handle);
    StrPrintF(balls_txt,"5");
    StrPrintF(maxthrow_txt,"7");
    StrPrintF(length_txt,"5");
    StrPrintF(multhrow_txt,"2");
    MemHandleUnlock(balls_handle);
    MemHandleUnlock(maxthrow_handle);
    MemHandleUnlock(length_handle);
    MemHandleUnlock(multhrow_handle);

    // set text fields
    form = FrmGetActiveForm();
    FldSetTextHandle(FrmGetObjectPtr(form,FrmGetObjectIndex(form,FldBalls)),balls_handle);
    FldSetTextHandle(FrmGetObjectPtr(form,FrmGetObjectIndex(form,FldMaxThrow)),maxthrow_handle);
    FldSetTextHandle(FrmGetObjectPtr(form,FrmGetObjectIndex(form,FldLength)),length_handle);
    FldSetTextHandle(FrmGetObjectPtr(form,FrmGetObjectIndex(form,FldMulThr)),multhrow_handle);

    // initialize check boxes
	FrmSetControlValue(form,FrmGetObjectIndex(form,cID_g_Seq),1);
	FrmSetControlValue(form,FrmGetObjectIndex(form,cID_g_Mul),0);
	FrmSetControlValue(form,FrmGetObjectIndex(form,cID_g_Fil),1);
	FrmSetControlValue(form,FrmGetObjectIndex(form,cID_g_11),1);

    FrmDrawForm(form);

	// initialize popup triggers
	CtlSetLabel(FrmGetObjectPtr(form,FrmGetObjectIndex(form,pID_mode)),
	  LstGetSelectionText(FrmGetObjectPtr(form,FrmGetObjectIndex(form,lID_mode)),0));
	CtlSetLabel(FrmGetObjectPtr(form,FrmGetObjectIndex(form,pID_type)),
	  LstGetSelectionText(FrmGetObjectPtr(form,FrmGetObjectIndex(form,lID_type)),1));
	CtlSetLabel(FrmGetObjectPtr(form,FrmGetObjectIndex(form,pID_state)),
	  LstGetSelectionText(FrmGetObjectPtr(form,FrmGetObjectIndex(form,lID_state)),0));

    // initialize popup vars
    popup_state = popup_mode = 0;
    popup_type = 1;

    handled = true;
    break;

  case appStopEvent:
  case frmCloseEvent:
    FrmReturnToForm( formTBasesm );
    handled = true;
    break;

  case ctlSelectEvent:

    if ( e->data.ctlEnter.controlID == bID_GssGen)
	{
	  // alloc output buffer (results_buffer) and determine parameters
	  // for generate_siteswaps function
	  results_buffer = (char *)MemPtrNew(RESULTS_BUFFERSIZE*sizeof(char));
	  info_buffer = (char *)MemPtrNew(INFO_BUFFERSIZE*sizeof(char));
	  *results_buffer = (char)0;
	  *info_buffer = (char)0;
	  error_string = "";
	  form = FrmGetActiveForm();
	  balls_txt = FldGetTextPtr(FrmGetObjectPtr(form, FrmGetObjectIndex(form, FldBalls)));
	  maxthrow_txt = FldGetTextPtr(FrmGetObjectPtr(form, FrmGetObjectIndex(form, FldMaxThrow)));
	  length_txt = FldGetTextPtr(FrmGetObjectPtr(form, FrmGetObjectIndex(form, FldLength)));
	  multhrow_txt = FldGetTextPtr(FrmGetObjectPtr(form, FrmGetObjectIndex(form, FldMulThr)));

	  if(!(CheckNums(balls_txt) && CheckNums(maxthrow_txt) && CheckNums(length_txt) && CheckNums(multhrow_txt)))
	  {
		 FrmAlert(errorGSSfields);
	     return true;  // don't generate siteswaps.  one of the fields is not valid.
	  }

	  balls    = StrAToI(balls_txt);
	  maxthrow = StrAToI(maxthrow_txt);
	  length   = StrAToI(length_txt);
	  multhrow = StrAToI(multhrow_txt);

      flags[0] = 1; // print number of patterns found (numflag)
      flags[1] = popup_state; // groundflag
	  flags[2] = popup_type; // fullflag

	  // lameflag
	  flags[3] = !FrmGetControlValue(form, FrmGetObjectIndex(form, cID_g_11));;

	  // sequence_flag
	  flags[4] = FrmGetControlValue(form, FrmGetObjectIndex(form, cID_g_Seq));

	  flags[5] = popup_mode; // mode (synch, asynch, or passing)

	  // mp_filter
	  flags[6] = FrmGetControlValue(form, FrmGetObjectIndex(form, cID_g_Fil));

	  // multiplex
	  if(FrmGetControlValue(form, FrmGetObjectIndex(form, cID_g_Mul)))
	    flags[7] = multhrow;
	  else
	    flags[7] = 1;

	  flags[8] = gss_delay; // delaytime
      flags[9] = gss_slot; // leader_person

      xlen[0] = gss_xself_len;  // pass inclusion and exclusion array lengths to generate_siteswaps()
      xlen[1] = gss_iself_len;  //
      xlen[2] = gss_xpass_len;  //

	  // execute generate_siteswaps and then bring up form to display results
	  generate_siteswaps(balls,maxthrow,length,flags,gss_xself,gss_iself,gss_xpass,xlen);
      FrmPopupForm(formTGenResults);
	  handled = true;
	}
    else if ( e->data.ctlEnter.controlID == bID_GssDone)
	{


      EventType event;

      MemSet(&event, sizeof(EventType), 0);
      event.eType = frmCloseEvent;
      event.data.frmClose.formID = FrmGetActiveFormID();
      EvtAddEventToQueue(&event);

	  handled = true;
	}
	else if ( e->data.ctlEnter.controlID == sID_other )
	{
	  FrmPopupForm(formTGSSother);
	  handled = true;
	}

	break;

  case popSelectEvent:
    if(e->data.popSelect.controlID == pID_mode)
      popup_mode = e->data.popSelect.selection;
    else if(e->data.popSelect.controlID == pID_type)
	  popup_type = e->data.popSelect.selection;
	else if(e->data.popSelect.controlID == pID_state)
	  popup_state = e->data.popSelect.selection;
	handled = false;
	break;

  default: // else do nothing
	break;
  }

  return handled;
}


/*
 * CopyIarrayToA()
 * copies int array to string
 ***************************************************/
Boolean CopyIarrayToA(Char s[],int a[],int length)
{
   int i;

   for(i=0;i<length;i++){
      if(a[i] >= 0 && a[i] <= 9)
	     s[i] = a[i] + '0';
	  else if(a[i] >= 10 && a[i] <= 35)
	     s[i] = a[i] + 'a' - 10;
	  else {
	     s[i] = (char)0;
	     return false; // invalid int array.
	  }
   }
   s[i] = (char)0;
   return true;
}


/*
 * CopyAarrayToI()
 * copies string to int array
 ***************************************************/
Boolean CopyAarrayToI(int a[],Char s[])
{
   int i;

   for(i=0;i<StrLen(s);i++){
      if(s[i] >= '0' && s[i] <= '9')
	     a[i] = s[i] - '0';
	  else if(s[i] >= 'a' && s[i] <= 'z')
	     a[i] = s[i] - 'a' + 10;
	  else if(s[i] >= 'A' && s[i] <= 'Z')
	     a[i] = s[i] - 'A' + 10;
	  else
	     return false; // invalid input.
      }
   return true;
}


/*
 * CheckAlphNums()
 * checks for valid alpha numeric strings. empty string is ok
 ***************************************************/
Boolean CheckAlphNums(Char s[]){

   int i;

   for(i=0;i<StrLen(s);i++){
      if(!((s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'z') || (s[i] >= 'A' && s[i] <= 'Z')))
         return false;
   }

   return true;
}


/*
 * CheckNums()
 * checks for valid numeric strings.  empty string is not valid.
 ***************************************************/
Boolean CheckNums(Char s[]){

   int i;

   if(StrLen(s) == 0) { return false; } // must have at least one digit
   for(i=0;i<StrLen(s);i++){            // must contain only numeric
      if(!(s[i] >= '0' && s[i] <= '9'))
         return false;
   }

   return true; // yes! valid number string
}


/* GSSotherEvent()
 * handles the events for the extra generate form
 ***************************************************/
static Boolean GSSotherEvent(EventPtr e)
{
  Boolean handled = false;
  FormPtr form;
//  UInt16 objIndex;
  MemHandle delay_Handle, slot_Handle, xself_Handle, iself_Handle, xpass_Handle;
  CharPtr delay_Pointer, slot_Pointer, xself_Pointer, iself_Pointer, xpass_Pointer;


  switch (e->eType) {

  case frmOpenEvent:
    // allocate memory for text handles (will be freed automatically later, part of form)
    xself_Handle = MemHandleNew(21*sizeof(char));
    iself_Handle = MemHandleNew(21*sizeof(char));
    xpass_Handle = MemHandleNew(21*sizeof(char));
    delay_Handle = MemHandleNew(3*sizeof(char));
    slot_Handle  = MemHandleNew(3*sizeof(char));

    // initialize all text
    xself_Pointer = MemHandleLock(xself_Handle);
    iself_Pointer = MemHandleLock(iself_Handle);
    xpass_Pointer = MemHandleLock(xpass_Handle);
    delay_Pointer = MemHandleLock(delay_Handle);
    slot_Pointer = MemHandleLock(slot_Handle);
    CopyIarrayToA(xself_Pointer, gss_xself, gss_xself_len);
    CopyIarrayToA(iself_Pointer, gss_iself, gss_iself_len);
    CopyIarrayToA(xpass_Pointer, gss_xpass, gss_xpass_len);
    StrIToA(delay_Pointer, gss_delay);
    StrIToA(slot_Pointer, gss_slot);
    MemHandleUnlock(xself_Handle);
    MemHandleUnlock(iself_Handle);
    MemHandleUnlock(xpass_Handle);
    MemHandleUnlock(delay_Handle);
    MemHandleUnlock(slot_Handle);

    // set text, set focus, draw form
    form = FrmGetActiveForm();
    FldSetTextHandle(FrmGetObjectPtr(form, FrmGetObjectIndex(form, FldXself)), xself_Handle);
    FldSetTextHandle(FrmGetObjectPtr(form, FrmGetObjectIndex(form, FldIself)), iself_Handle);
    FldSetTextHandle(FrmGetObjectPtr(form, FrmGetObjectIndex(form, FldXpass)), xpass_Handle);
    FldSetTextHandle(FrmGetObjectPtr(form, FrmGetObjectIndex(form, FldDelay)), delay_Handle);
    FldSetTextHandle(FrmGetObjectPtr(form, FrmGetObjectIndex(form, FldSlot)), slot_Handle);
    FrmSetFocus(form, FrmGetObjectIndex(form, FldXself));
    FrmDrawForm(form);

    handled = true;
    break;

  case appStopEvent:
  case frmCloseEvent:
    FrmReturnToForm(formTGSS);
    handled = true;
    break;

  case ctlSelectEvent:

    if ( e->data.ctlEnter.controlID == bID_otherClose){

      // get pointers to text fields
      form = FrmGetActiveForm();
      delay_Pointer = FldGetTextPtr(FrmGetObjectPtr(form, FrmGetObjectIndex(form, FldDelay)));
      slot_Pointer  = FldGetTextPtr(FrmGetObjectPtr(form, FrmGetObjectIndex(form, FldSlot)));
      xself_Pointer = FldGetTextPtr(FrmGetObjectPtr(form, FrmGetObjectIndex(form, FldXself)));
      iself_Pointer = FldGetTextPtr(FrmGetObjectPtr(form, FrmGetObjectIndex(form, FldIself)));
      xpass_Pointer = FldGetTextPtr(FrmGetObjectPtr(form, FrmGetObjectIndex(form, FldXpass)));

	  if(!(CheckNums(delay_Pointer) && CheckNums(slot_Pointer))){
	     FrmAlert(errorDelaySlot);
	     return true;   // invalid field(s) don't exit form
	  }
	  if(!(CheckAlphNums(xself_Pointer) && CheckAlphNums(iself_Pointer) && CheckAlphNums(xpass_Pointer))){
		  FrmAlert(errorOtherThrows);
		  return true;   // invalid field(s) don't exit form
	  }

	  gss_delay = StrAToI(delay_Pointer);
	  gss_slot  = StrAToI(slot_Pointer);
	  CopyAarrayToI(gss_xself, xself_Pointer);
	  CopyAarrayToI(gss_iself, iself_Pointer);
	  CopyAarrayToI(gss_xpass, xpass_Pointer);

      gss_xself_len = StrLen(xself_Pointer);
      gss_iself_len = StrLen(iself_Pointer);
      gss_xpass_len = StrLen(xpass_Pointer);

      FrmReturnToForm(formTGSS);
      handled = true;
	}
    else if ( e->data.ctlEnter.controlID == bID_otherCancel){
      FrmReturnToForm(formTGSS);
      handled = true;
	}
	break;

  default: // else do nothing
	break;
  }

  return handled;
}


/* ResultsEvent()
 * Display the Generated siteswaps
 *********************************/
static Boolean ResultsEvent(EventPtr e)
{
  FormPtr form;

  FieldPtr field;
  Boolean handled = false;


  switch (e->eType) {

	case frmOpenEvent:
	  form = FrmGetActiveForm();
	  FrmDrawForm(form);

      // draw results output
	  field = FrmGetObjectPtr(form,FrmGetObjectIndex(form,resultsField));
	  FldSetTextPtr(field,results_buffer);
	  FldRecalculateField(field,true);
	  FldDrawField (field);

	  // draw info and error strings
	  WinDrawChars(info_buffer,StrLen(info_buffer),72,137);
	  WinDrawChars(error_string,StrLen(error_string),7,124);

	  UpdateScrollbar(resultsScrollBar, resultsField);
	  break;

	case ctlSelectEvent:
	  if ( e->data.ctlEnter.controlID == bID_resultsClose ) {
		FrmReturnToForm( formTGSS );
		MemPtrFree(results_buffer);
		handled = true;
	  }
	  if ( e->data.ctlEnter.controlID == bID_resultsCopy ) {
		form = FrmGetActiveForm();
		FldCopy(FrmGetObjectPtr(form,FrmGetObjectIndex(form,resultsField)));
		handled = true;
      }
	  break;

	case keyDownEvent:
	  if (e->data.keyDown.chr == pageUpChr) {
		PageScroll(winUp, resultsScrollBar, resultsField);
		handled = true;
	  } else if (e->data.keyDown.chr == pageDownChr) {
		PageScroll(winDown, resultsScrollBar, resultsField);
		handled = true;
	  }
	  break;

	case sclRepeatEvent:
	  ScrollLines(e->data.sclRepeat.newValue - e->data.sclRepeat.value, resultsScrollBar, resultsField, false);
	  break;

	default: // else do nothing
	  break;
  }

  return handled;

}


// HangingAround()
static long HanginAround(void)
{
  if ( pref_Pause  )
	return evtWaitForever;
  return 2;
}

/* PilotMain()
 * handles the toplevel command and event dispatch.
 *****************************************************/
DWord PilotMain(Word cmd, Ptr cmdPBP, Word launchFlags)
{
  EventType e;
  Word error;
  FormPtr frm;
  unsigned int formID;


  if (cmd == sysAppLaunchCmdNormalLaunch) {

	// Allocate memory for global vars so that it can be deallocated later
	pattern = ALLOC(int, 1);
    balls = ALLOC(int, 1);

    /*
    ** Launch the first form.
    */
          wchItm = "5";
		  StrCopy(wchItmString,wchItm);
          jug_setup(wchItm);
	FrmGotoForm(formTBasesm);


    /*
    ** Process events.
    */
	while(1) {

	  EvtGetEvent(&e, HanginAround() );


	  switch (e.eType) {

		case winExitEvent:
		  if ( e.data.winExit.exitWindow == (WinHandle) FrmGetFormPtr(formTBasesm))
		  {
			// pause when using menu etc..
			pref_Pause = true;
		  }
          break;

		case winEnterEvent:
		  if ( e.data.winEnter.enterWindow == (WinHandle) FrmGetFormPtr(formTBasesm) &&
			  e.data.winEnter.enterWindow == (WinHandle) FrmGetFirstForm())
		  {
			// restore pause value
			frm = FrmGetActiveForm();
			pref_Pause = FrmGetControlValue(frm,FrmGetObjectIndex(frm,bID_Pause));
		  }
          break;

		case frmLoadEvent:
		  frm = FrmInitForm(formID = e.data.frmLoad.formID);
		  FrmSetActiveForm(frm);

		  switch (formID) {

			case formTStart:
			  FrmSetEventHandler(frm, InitialJugEvent);
			  break;

			case formTBasesm:
			  FrmSetEventHandler(frm, JuggleEvent);
			  break;

			case aboutBasesm:
			  FrmSetEventHandler(frm, AboutEvent);
			  break;

			case helpBasesm:
			  FrmSetEventHandler(frm, HelpEvent);
			  break;

			case formTSelectSS:
			  FrmSetEventHandler(frm, SelectSSEvent);
			  break;

			case formTInputSS:
			  FrmSetEventHandler(frm, SSInputEvent);
			  break;

			case formTOptions:
			  FrmSetEventHandler(frm, OptionsEvent);
			  break;

			case formTGSS:
			  FrmSetEventHandler(frm, GenerateEvent);
			  break;

			case formTGSSother:
			  FrmSetEventHandler(frm, GSSotherEvent);
			  break;

			case formTGenResults:
			  FrmSetEventHandler(frm, ResultsEvent);
			  break;
		  }

          break;

		case appStopEvent:
		  return 0;
		  break;

        default: // else do nothing
		  break;
	  }

	  if ( !SysHandleEvent(&e))
		if ( !MenuHandleEvent(NULL, &e, &error))
		  FrmDispatchEvent(&e);

	} while (e.eType != appStopEvent);

    /*
    ** Stop application.
    */
//    FrmCloseAllForms();
  }

  return 0;

}


void JuggleSounds (int freq)
{
	SndCommandType		sndCmd;
        UInt SoundAmp;

        SndGetDefaultVolume(NULL, &SoundAmp, NULL);

	sndCmd.cmd = sndCmdFreqDurationAmp;
	sndCmd.param1 = freq;
	sndCmd.param2 = 50; /* duration mS */
	sndCmd.param3 = 50; /* Amplitude */
	SndDoCmd( 0, &sndCmd, true/*noWait*/ );
}


