/*
 * Screen calc - Palm Pilot graphical calculator (heh, the 4Mb in my
 * IIIx makes my TI-82 look a bit crap)
 * Copyright (C) 2000 Andrew Hunter
 *
 *  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
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */

#include <PalmOS.h>
#include <stdarg.h>

#include "resource.h"
#include "MathLib.h"

#include "calc.h"
#include "parse.h"
#include "number.h"

/*                          ----=====88888=====----                          */

void CheckPrefs(CalcPrefs* prefs, Boolean saved)
{
  UInt16     res;
  UInt16     size;

  size = sizeof(CalcPrefs);

  res = PrefGetAppPreferences(calcAppCreator, 0, prefs, &size, saved);
  if (res == noPreferenceFound || size != sizeof(CalcPrefs))
    {
      if (size != sizeof(CalcPrefs))
	{
	  FrmAlert(prefNotice);
	}

      /* Set the defaults */
      prefs->licenceAccept = 0;
      
      SetPrefs(prefs, saved);
    }
}

void SetPrefs(CalcPrefs* prefs, Boolean saved)
{
  PrefSetAppPreferences(calcAppCreator, 0, calcAppVersion,
			prefs, (UInt16) sizeof(CalcPrefs),
			saved);
}

/*                          ----=====88888=====----                          */

static Boolean AboutCalcEvent(EventPtr event)
{
  FormType* frm;

  frm = FrmGetActiveForm();

  switch (event->eType)
    {
    case ctlEnterEvent:     
      switch (event->data.ctlEnter.controlID)
	{
	case aboutOK:
	  FrmReturnToForm(screenCalcMain);
	  FrmSetActiveForm(FrmGetFormPtr(screenCalcMain));
	  break;
	}
      return true;
    }

  return false;
}

/*                          ----=====88888=====----                          */

/* Calculator text display functions */

int textX, textY;

/* Calculator main window */

static Boolean stop;

/*                          ----=====88888=====----                          */

/* The text display */

static MemHandle displayContents;
UInt32           displayContLen;

void CalcSetScrollBar(FormPtr frm)
{
  FieldPtr     field;
  ScrollBarPtr scroll;
  UInt16       scrollPos, textHeight, fieldHeight;
  UInt16       maxValue;
  
  field  = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, displayField));
  scroll = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, displayScroll));
 
  FldGetScrollValues(field, &scrollPos, &textHeight, &fieldHeight);

  if (textHeight > fieldHeight)
    maxValue = textHeight - fieldHeight;
  else if (scrollPos)
    maxValue = scrollPos;
  else
    maxValue = 0;

  SclSetScrollBar(scroll, scrollPos, 0, maxValue, fieldHeight-1);
}

void CalcScrollToBottom(FormPtr frm)
{
  FieldPtr      field;

  field  = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, displayField));

  FldScrollField(field, 16384, winDown);
  
  CalcSetScrollBar(frm);
}

void CalcPrint(Char* format, ...)
{
  FieldPtr     field;
  FormPtr      frm;
  Char*        contents;
  UInt16       newlen, spos;
  Char*        string;
  va_list      args;

  string = MemPtrNew(512*sizeof(Char));
  if (!string)
    return;

  va_start(args, format);
  StrVPrintF(string, format, args);
  va_end(args);

  frm    = FrmGetFormPtr(screenCalcMain);
  field  = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, displayField));

  newlen = StrLen(string);
  displayContents = FldGetTextHandle(field);

  spos = FldGetScrollPosition(field);
  FldSetTextHandle(field, NULL);

  if (displayContLen > 1024)
    {
      displayContLen -= newlen;
      contents = MemHandleLock(displayContents);
      MemMove(contents, contents + newlen, displayContLen+1);
      contents[displayContLen] = 0;
      MemHandleUnlock(displayContents);
    }
  else if (MemHandleResize(displayContents,
		      (displayContLen+newlen+1)*sizeof(Char)))
    {
      MemPtrFree(string);
      /* Doh! */
      FldSetTextHandle(field, displayContents);
      FrmAlert(debugNotice);
      return;
    }

  contents = MemHandleLock(displayContents);
  StrCat(contents, string);
  displayContLen += newlen;
  MemHandleUnlock(displayContents);

  FldSetTextHandle(field, displayContents);
  FldSetScrollPosition(field, spos);
  FldDrawField(field);

  CalcScrollToBottom(frm);

  MemPtrFree(string);
}

/*                          ----=====88888=====----                          */

static Char buffer[256];

void PrintTree(ParseTree* tree)
{
  if (tree == NULL)
    return;
  
  switch (tree->nodeData.type)
    {
    case Operator:
      CalcPrint("Operator '%c'\n",
		tree->nodeData.data.operator);
      break;
      
    case Number:
    case Variable:
      FormatDouble(tree->nodeData.data.number,
		   buffer,
		   256,
		   FormatFloat,
		   10);
      CalcPrint("Number %s\n", buffer);
      break;

    default:
      CalcPrint("Unknown\n");
      return;
    }

  if (tree->left)
    {
      CalcPrint("Left node:\n");
      PrintTree(tree->left);
    }
  if (tree->right)
    {
      CalcPrint("Right node:\n");
      PrintTree(tree->right);
    }
  CalcPrint("Node end\n");
}

/* Deal with main form events */
static Boolean CalcMainHandleEvent(EventPtr event)
{
  FormType* frm;
  UInt32 tick;

  frm = FrmGetActiveForm();
  
  switch (event->eType)
    {
    case frmOpenEvent:
      FrmDrawForm(frm);
      DrawCalc(frm);
      return true;

    case frmUpdateEvent:
      FrmDrawForm(frm);
      DrawCalc(frm);
      return true;

    case fldChangedEvent:
      if (event->data.fldChanged.fieldID == displayField)
	{
	  CalcSetScrollBar(frm);
	}
      break;

    case sclRepeatEvent:
      {
	Int16 lines;
	FieldPtr field;

	lines = event->data.sclRepeat.newValue - event->data.sclRepeat.value;
	
	field = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm,
						       displayField));
	FldScrollField(field,
		       lines>=0?lines:-lines,
		       lines>=0?winDown:winUp);
      }
      break;

    case ctlEnterEvent:
      if (frm != NULL)
	FrmHandleEvent(frm, event);

      switch (event->data.ctlEnter.controlID)
	{
	case scmDone:
	  stop = true;
	  break;
	}
      return true;

    case menuEvent:
      switch (event->data.menu.itemID)
	{
	case aboutOption:
	  FrmPopupForm(aboutCalc);
	  break;
	  
	case licenceOption:
	  FrmHelp(gnuLicence);
	  break;
	      
	case helpOption:
	  FrmHelp(helpText);
	  break;
	}
      return true;

    case keyDownEvent:
      if (FrmGetObjectId(frm, FrmGetFocus(frm)) == inputField)
	{
	  FieldPtr  field;
	  Char*     expression;

	  field = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, inputField));
	  
	  switch (event->data.keyDown.chr)
	    {
	    case 10:
	    case 13:
	      expression = FldGetTextPtr(field);
	      CalcPrint("=> %s\n", expression);

	      {
#ifndef LEXDEBUG
		ParseTree* tree;
		double result;

		tree = Parser(expression);
		if (ParseError() != NULL)
		  {
		    CalcPrint("%s\n", ParseError());
		  }
		else
		  {
		    result = Evaluate(tree);
		    FormatDouble(result,
				 buffer,
				 256,
				 FormatFloat,
				 10);
		    CalcPrint("%s\n", buffer);
		  }

		ParseFinished();
#else
		YYSTYPE data;
		ParserData pdata;
		int res;

		pdata.nextPos = 0;
		pdata.string = expression;

		do {
		  res = Lexer(&data, &pdata);

		  CalcPrint("%i ", res);
		} while (res != 0);
		CalcPrint("\n");
#endif
	      }
	      
	      FldDelete(field, 0, 64);
	      return true;
	    }
	}
      else
	{
	  CalcPrint("Burp\n");
	}
      break;
    }

  return false;
}

void DrawCalc(FormType* calc)
{
  WinHandle win;
  Boolean cursor;
  UInt x;

  /* Set the drawing window */
  win = FrmGetWindowHandle(calc);
  WinSetDrawWindow(win);

  /* Draw the line borders */
  WinDrawLine(0, 144, 160, 144);
  WinDrawLine(0, 16, 160, 16);
}

/*                          ----=====88888=====----                          */

void RunApplication(void)
{
  UInt16    error, frm;
  EventType event;
  FormType* pfrm;
  UInt16 x;

  /* Start up MathLib */
  error = SysLibFind(MathLibName, &MathLibRef);
  if (error)
    error = SysLibLoad(LibType, MathLibCreator, &MathLibRef);
  if (error)
    {
      FrmAlert(mathLibNotice);
      return;
    }
  error = MathLibOpen(MathLibRef, MathLibVersion);
  if (error)
    {
      FrmAlert(mathLibError);
      return;
    }

  /* Initialise the global variables */
  stop = false;

  /* Go to the main screen */
  FrmGotoForm(screenCalcMain);

  /* Event loop */
  do
    {
      Int32 timeout;

      timeout = evtWaitForever;
      
      /* Around we go */
      EvtGetEvent(&event, timeout);

      /* Automatic event handling */
      if (!SysHandleEvent(&event))
	MenuHandleEvent(NULL, &event, &error);

      /* Our own event handling */
      switch (event.eType)
	{
	case frmLoadEvent:
	  /* Form loaded; lets activate it */
	  FrmSetActiveForm(FrmInitForm(event.data.frmLoad.formID));
	  break;

	case frmOpenEvent:
	  /* Form opened; activate & draw it */
	  pfrm = FrmGetActiveForm();
	  FrmDrawForm(pfrm);

	  switch (FrmGetActiveFormID())
	    {
	    case screenCalcMain:
	      /* Draw the special calucator stuff */
	      DrawCalc(pfrm);

	      {
		FieldPtr  field;
		MemHandle oldDisplay;
		Char*     fconts;

		/* Set the input focus */
		field = FrmGetObjectPtr(pfrm, FrmGetObjectIndex(pfrm,
								inputField));
		FrmSetFocus(pfrm, FrmGetObjectIndex(pfrm, inputField));

		/* Set up the display */
		field = FrmGetObjectPtr(pfrm, FrmGetObjectIndex(pfrm,
								displayField));

		oldDisplay = FldGetTextHandle(field);
		
		displayContents = MemHandleNew(sizeof(Char)*1);
		fconts = MemHandleLock(displayContents);
		fconts[0] = '\0';
		displayContLen = 0;
		MemHandleUnlock(displayContents);
		FldSetTextHandle(field, displayContents);
		
		if (oldDisplay != NULL)
		  MemHandleFree(oldDisplay);

		CalcPrint("Calculator v0.1.0 (GPL)\n\n\xa9 Andrew Hunter, 2000\n\n");
	      }
	      break;
	    }
	  break;

	default:
	  /* Otherwise... */
	  pfrm = FrmGetActiveForm();
	  if (pfrm != NULL)
	    {
	      Boolean result = false;
	      UInt16  active;

	      active = FrmGetActiveFormID();

	      /* Call the routine appropriate to this form */
	      switch (active)
		{
		case screenCalcMain:
		  result = CalcMainHandleEvent(&event);
		  break;

		case aboutCalc:
		  result = AboutCalcEvent(&event);
		  break;
		}

	      if (!result)
		FrmHandleEvent(pfrm, &event);
	    }
	}
    }
  while (event.eType != appStopEvent && !stop);

  /* Tidy up */
  FrmCloseAllForms();

  /*
   * FIXME: PalmOS frees all handles, but it would be good form to
   * free up the display here anyway
   */
}

/*                          ----=====88888=====----                          */

UInt16 confirmed = 0;

UInt32 PilotMain(UInt16 cmd, void *cmdPBP, UInt16 launchFlags)
{
  CalcPrefs prefs;
  
  switch (cmd)
    {
    case sysAppLaunchCmdNormalLaunch:
      CheckPrefs(&prefs, true);
      
      if (prefs.licenceAccept == 0)
	{
	  UInt16 button;
	  
	  button = FrmAlert(copyrightNotice);

	  if (button == 0)
	    {
	      prefs.licenceAccept = 1;
	      SetPrefs(&prefs, true);

	      RunApplication();
	    }
	  else
	    {
	      return 0;
	    }
	}
      else
	{
	  RunApplication();
	}
      break;
      
    default:
      return 0;
    }
  
  return 0;
}

/*                          ----===== EOF =====----                          */
