/*
 *  GEARS : Bicycle gear ratio Calculator
 *
 *    Version 1
 *
 *    by Joel Swank 9-19-89
 *
 */

/********* INCLUDES ************************ */


#include <exec/types.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <intuition/intuition.h>
#include <stdio.h>
#include "fio.h"


/* Data in helpwin.h  */
extern struct IntuiText prfailtxt ;
extern struct IntuiText badfiletxt ;
extern struct IntuiText fiofailtxt ;
extern struct IntuiText winfailtxt ;
extern struct IntuiText lockfailtxt ;
extern struct IntuiText geartxt ;
extern struct IntuiText infailtxt ;
extern struct IntuiText infailtxt2 ;
extern struct IntuiText oktxt ;
extern struct IntuiText cantxt ;
extern struct IntuiText retrytxt ;
extern struct NewWindow	NewWindowStructureHelp;
extern struct NewWindow	NewWindowStructure3;
extern char gear_buff[];

/* Data in doargs.c   */
extern char filename[];

/* Defines for setting/clearing GADGDISABLED flag */
#define OffGad(gad) (gad).Flags = gad.Flags | GADGDISABLED
#define OnGad(gad) (gad).Flags = gad.Flags & ~(GADGDISABLED)
#define OffMenu(item) (item).Flags = item.Flags & ~(CHECKED)
#define OnMenu(item) (item).Flags = item.Flags | CHECKED



extern struct IntuitionBase *IntuitionBase ;
extern struct GfxBase *GfxBase ;
extern struct DosLibrary *DosBase ;

struct Screen *OpenScreen();
struct Screen *Sc = NULL;
struct ViewPort vP;
struct Window *wG = NULL;
struct Window *wF = NULL;
struct RastPort *rpG;
struct IntuiMessage *message;	/* the message from the IDCMP */

struct Window *OpenWindow();
void *OpenLibrary();
struct IntuiMessage *GetMsg();
struct MenuItem *ItemAddress();

struct FileIOSupport *FIOSupp = NULL;      /* fileio support stuff */
struct FileIOSupport *GetFileIOSupport();
struct FileLock *Lock();
char save_title[] = "Select File to SAVE";
char load_title[] = "Select File to LOAD";

/* get the PowerWindows 2.0 code */
#include "gearscreen.h"

/* internal data areas */
int front = 2;      /* number of front gears */
int rear = 6;       /* number of rear gears */
float ratios[21];   /* storage for ratios    */
float front_gear[3];/* storage for front gears */
float rear_gear[7]; /* storage for rear gears */
int av[21];         /* indices for sort      */
char cbuf[21][8];   /* storage for gear combos */
int count = 0;      /* count of ratios        */
int diameter = 27;  /* wheel diameter from args */
long color[8];    /* savearea for colors  */
struct ColorMap *cm; /* pointer to screen colors */
char rearmsg[] = "Bad Rear Gear: ";
char frontmsg[] = "Bad Front Gear: ";


main(argc,argv)
int argc;
char *argv[];
{
	UWORD code;
	ULONG class;
	APTR object;
	FILE *fp;
 

	/* Open the libraries */

	IntuitionBase = (struct IntuitionBase *)
		OpenLibrary("intuition.library", 0L);
	if (IntuitionBase == NULL)
		{
		done(11);
		}
	GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0L);
	if (GfxBase == NULL)
		{
		done(10);
		}

	DosBase = (struct DosLibrary *)OpenLibrary("dos.library", 0);
	if (DosBase == NULL)
		{
		done(16);
		}

	/*   Get the arguments */

	filename[0] = '\0';
	if (argc == 0) getWBargs();
	else getCLIargs(argc,argv);
	Diameter_GadSInfo.LongInt = diameter;
	sprintf(Diameter_GadSIBuff,"%d",diameter);

	/* Open my screen      */

	Sc = OpenScreen(&NewScreenStructure);
	if ( Sc == NULL )
		{
		done(15);
		}
	vP = Sc->ViewPort;
	LoadRGB4(&vP,&Palette,PaletteColorCount);

	/* tie windows to my screen */

	NewWindowStructureHelp.Screen = Sc;
	NewWindowStructure3.Screen = Sc;
	NewWindowStructure1.Screen = Sc;

	wG = OpenWindow(&NewWindowStructure1);	/* open main window */
	if ( wG == NULL )
		{
		done(12);
		}

	rpG = wG->RPort;	/* get a rastport pointer for the window */

	PrintIText(rpG,&IntuiTextList1,0L,0L); /* write window text */

	SetMenuStrip(wG,&MenuList1);	/* attach my Menu */

	if (filename[0] != '\0')
		{
		while ((fp = fopen(filename,"r")) == NULL)
			{
			infailtxt2.IText = (UBYTE *) filename;
			if (AutoRequest(wG,&infailtxt,&retrytxt,&cantxt,
				0L,0L,reqlen(filename),75L)) continue;
			}
		if (fp != NULL) {
			read_file(fp);
			fclose(fp);
			}
		}

	/* Wait for some User interaction */
	while(1)
	{
		WaitPort(wG->UserPort);
			while( (message = (struct IntuiMessage *)
				GetMsg(wG->UserPort) ) != NULL)
			{
				code = message->Code;  /* MENUNUM */
				object = message->IAddress;  /* Gadget */
				class = message->Class;  /* IDCMP Flags */
				ReplyMsg(message);  /* Free the sender */
				switch (class)
					{
					case CLOSEWINDOW :
						done(0);   /* close gadget clicked */
					case GADGETUP:
						if (object == (APTR) &Calc_Gad)
							{
							calc_it();
							display_it();
							break;
							}
						/* add more gadget checks here */
						break;
					case MENUPICK:    /* menu selection made */
						/* I get a NULL message whenever the user brings
						   up the menu but doesn't select anything    */
						if (code != MENUNULL) do_pick((USHORT)code);
						break;
					}
			}
	} 
}

/*
 * Cleanup and exit
 */

done(how)
int how;
{
	if (FIOSupp) ReleaseFileIO(FIOSupp);
	if (wG) ClearMenuStrip(wG);
	if (wG) CloseWindow(wG);
	if (Sc) CloseScreen(Sc);
	if (DosBase) CloseLibrary(DosBase);
	if (GfxBase != NULL) CloseLibrary(GfxBase);
	if (IntuitionBase != NULL) CloseLibrary(IntuitionBase);
	exit(how);

 /* exit codes 
  * 0  User requested
  * 10 graphics lib open fail
  * 11 intuition lib open fail
  * 12 main window open fail
  * 13 _abort() called
  * 14 Bad CLI args
  * 15 Custom Screen open Fail
  * 16 Dos lib open fail
  */
}


/*
 * do_pick : handle chain of menu selections
 */

do_pick(menunum)
USHORT menunum;
{
struct MenuItem *item, *ItemAddress();
	while (menunum != MENUNULL)
		{
		switch(MENUNUM(menunum))
			{
			case 0:     /* Project Menu */
			switch(ITEMNUM(menunum))
				{
				case 0: /* Load */
					do_load();
					break;
				case 1: /* Save */
					do_save();
					break;
				case 2: /* Print */
					do_print();
					break;
				case 3: /* Colors */
					ClearMenuStrip(wG);
					DoColorWindow(Sc,170,15,0,TRUE);
					SetMenuStrip(wG,&MenuList1);	/* re-attach my Menu */
					break;
				case 4: /* Help */
					help();
					break;
				case 5: /* About */
					about();
					break;
				case 6: /* Quit */
					done(0);
				}
			break;
			case 1:     /* Gears Menu */
			switch(ITEMNUM(menunum))
				{
				case 0:     /* Front Gear selection */

				/* Three mutually excluded items. intuition keeps track, 
				   I have to enable/disable integer input gadgets and
				   redraw the window         */

				switch(SUBNUM(menunum))
					{
					case 0:   /* 1 */
						front = 1;
						break;
					case 1:   /* 2 */
						front = 2;
						break;
					case 2:   /* 3 */
						front = 3;
						break;
					}
				break;
				case 1:     /* Rear Gear selection */

				/* Five mutually excluded items. intuition keeps track, 
				   I have to enable/disable integer input gadgets and
				   redraw the window         */

				switch(SUBNUM(menunum))
					{
					case 0:   /* 3 */
						rear = 3;
						break;
					case 1:   /* 4 */
						rear = 4;
						break;
					case 2:   /* 5 */
						rear = 5;
						break;
					case 3:   /* 6 */
						rear = 6;
						break;
					case 4:   /* 7 */
						rear = 7;
						break;
					}
				break;
				}
			set_gads();
			redraw_scr();
			break;
			default:	/* What's this garbage ? */
				menunum = MENUNULL;
			}       /* end switch MENUNUM  */

		/* Get chain to next selection. NextSelect contains another item
		   when the user makes multiple menu selections             */
		item = ItemAddress(&MenuList1,(long) menunum);
		menunum = item->NextSelect;
		}
}


/*
 *  Calculate and display gear table
 */

calc_it()
{
	int i,j;
	if (!check_gears()) return; /* validate the gears */

	count = 0;                 /* Calculate the ratios */
	for (i=0; i<front; i++)
		{
		for (j=0; j<rear; j++)
			{
			ratios[count] = front_gear[i]/rear_gear[j]
			        * Diameter_GadSInfo.LongInt;
			sprintf(cbuf[count],"%2.0fx%2.0f", front_gear[i], rear_gear[j]);
			count++;
			}
		}

	for (i=0; i<21; i++) av[i] = i; /* init index array */
	QuickSort(av, count);           /* sort the indices */
}


/*
 * check_gears : validate reasonable values for all active gears
 */

check_gears()
{
	int i;

	/* capture all gear values from string gadgets */
	front_gear[0] = Front1_GadSInfo.LongInt;
	front_gear[1] = Front2_GadSInfo.LongInt;
	front_gear[2] = Front3_GadSInfo.LongInt;
	rear_gear[0] = Rear1_GadSInfo.LongInt;
	rear_gear[1] = Rear2_GadSInfo.LongInt;
	rear_gear[2] = Rear3_GadSInfo.LongInt;
	rear_gear[3] = Rear4_GadSInfo.LongInt;
	rear_gear[4] = Rear5_GadSInfo.LongInt;
	rear_gear[5] = Rear6_GadSInfo.LongInt;
	rear_gear[6] = Rear7_GadSInfo.LongInt;

	for (i=0;i<rear;i++)
		if (rear_gear[i] < 10.0 || rear_gear[i] > 40.0)
			{
			sprintf(gear_buff,"%s%2.0f",rearmsg,rear_gear[i]);
			AutoRequest(wG,&geartxt,0L,&oktxt,0L,0L,300L,75L);
			return(FALSE);
			}
	for (i=0;i<front;i++)
		if (front_gear[i] < 10.0 || front_gear[i] > 80.0)
			{
			sprintf(gear_buff,"%s%2.0f",frontmsg,front_gear[i]);
			AutoRequest(wG,&geartxt,0L,&oktxt,0L,0L,300L,75L);
			return(FALSE);
			}
	return(TRUE);
}

/*
 *   Set Disabled bit on all gear string gadgets 
 */

set_gads()
{
	if (front > 1) OnGad(Front2_Gad);
	else OffGad(Front2_Gad);
	if (front > 2) OnGad(Front3_Gad);
	else OffGad(Front3_Gad);
	if (rear > 3) OnGad(Rear4_Gad);
	else OffGad(Rear4_Gad);
	if (rear > 4) OnGad(Rear5_Gad);
	else OffGad(Rear5_Gad);
	if (rear > 5) OnGad(Rear6_Gad);
	else OffGad(Rear6_Gad);
	if (rear > 6) OnGad(Rear7_Gad);
	else OffGad(Rear7_Gad);
}

/*
 *   Set the menus according to front and rear
 */

set_menus()
{
	if (rear == 3) OnMenu(SubItem1);
	else OffMenu(SubItem1);
	if (rear == 4) OnMenu(SubItem2);
	else OffMenu(SubItem2);
	if (rear == 5) OnMenu(SubItem3);
	else OffMenu(SubItem3);
	if (rear == 6) OnMenu(SubItem4);
	else OffMenu(SubItem4);
	if (rear == 7) OnMenu(SubItem5);
	else OffMenu(SubItem5);
	if (front == 1) OnMenu(SubItem6);
	else OffMenu(SubItem6);
	if (front == 2) OnMenu(SubItem7);
	else OffMenu(SubItem7);
	if (front == 3) OnMenu(SubItem8);
	else OffMenu(SubItem8);
}

/*
 *    Display ratios in main window
 *    (Assumes calc_it has been done)
 */

#define XPOS 170
#define LINESIZE 9

char header[] = "Gear  Wheel Size        Spacing";
char disp_buff[100], temp_buff[30];

display_it()
{
	int ypos = 20, i, j;

	SetAPen(rpG,7L);  /* set draw color */
	Move(rpG,XPOS,ypos);
	Text(rpG,header,sizeof(header)-1);
	ypos += LINESIZE;
	for (i=0; i<count; i++, ypos += LINESIZE)
		{
		bld_buf(i);
		Move(rpG,XPOS,ypos);
		Text(rpG,disp_buff,strlen(disp_buff));
		}
}

/*
 *    Print the table
 */

do_print()
{
	int i;
	FILE *fp;

	while ((fp = fopen("PRT:","w")) == NULL)
		{
		if (AutoRequest(wG,&prfailtxt,&retrytxt,&cantxt,
			0L,0L,300L,75L)) continue;
		return;
		}

	fprintf(fp,"%s\n",header);

	for (i=0; i<count; i++)
		{
		bld_buf(i);
		fprintf(fp,"%s\n",disp_buff);
		}
	fclose(fp);
}

/*
 *    Build a line of print
 */

bld_buf(i)
int i;
{
		int j;

		j = av[i];
		sprintf(disp_buff,"%s   %7.2f",cbuf[j],ratios[j]);
		if (i>0) {
			sprintf(temp_buff,"     %6.2f",ratios[j] - ratios[av[i-1]]);
			strcat(disp_buff,temp_buff);
			}
		if (i>1) {
			sprintf(temp_buff,"     %6.2f",ratios[j] - ratios[av[i-2]]);
			strcat(disp_buff,temp_buff);
			}
		if (i>2) {
			sprintf(temp_buff,"     %6.2f",ratios[j] - ratios[av[i-3]]);
			strcat(disp_buff,temp_buff);
			}
}

/*
 *    Load data from an ASCII file
 */

do_load()
{
	FILE *fp;

	select_file(0);
	if (filename[0] == '\0') return;

	while ((fp = fopen(filename,"r")) == NULL)
		{
		infailtxt2.IText = (UBYTE *) filename;
		if (AutoRequest(wG,&infailtxt,&retrytxt,&cantxt,
			0L,0L,reqlen(filename),75L)) continue;
		return;
		}
	read_file(fp);
	fclose(fp);
}


/*
 *    Read in data from open file
 */

read_file(fp)
FILE *fp;
{
	int i;
	char x;
	int fr = front;
	int re = rear;

	i = fscanf(fp,"%d, %d",&front,&rear);
	if (i != 2 || rear < 3 || rear >7 || front < 1 || front >3)
		{
		infailtxt2.IText = (UBYTE *) filename;
		AutoRequest(wG,&badfiletxt,0L,&oktxt,0L,0L,reqlen(filename),75L);
		front = fr;
		rear = re;
		return(FALSE);
		}
	fscanf(fp,", %d ,",&Diameter_GadSInfo.LongInt);
	sprintf(Diameter_GadSIBuff,"%d",Diameter_GadSInfo.LongInt);
	fscanf(fp,"%c",&x);
	if (x == 'm') inmm_Gad.Flags |= SELECTED;
	if (x == 'i') inmm_Gad.Flags &= ~SELECTED;
	for (i=0;i<front;i++) fscanf(fp,",%2f",&front_gear[i]);
	for (i=0;i<rear;i++) fscanf(fp,",%2f",&rear_gear[i]);

	/*   Put the data in the string gadgets */
	Front1_GadSInfo.LongInt = (long) front_gear[0];
	sprintf(Front1_GadSIBuff,"%d",Front1_GadSInfo.LongInt);
	Front2_GadSInfo.LongInt = (long) front_gear[1];
	sprintf(Front2_GadSIBuff,"%d",Front2_GadSInfo.LongInt);
	Front3_GadSInfo.LongInt = (long) front_gear[2];
	sprintf(Front3_GadSIBuff,"%d",Front3_GadSInfo.LongInt);

	Rear1_GadSInfo.LongInt = (long) rear_gear[0];
	sprintf(Rear1_GadSIBuff,"%d",Rear1_GadSInfo.LongInt);
	Rear2_GadSInfo.LongInt = (long) rear_gear[1];
	sprintf(Rear2_GadSIBuff,"%d",Rear2_GadSInfo.LongInt);
	Rear3_GadSInfo.LongInt = (long) rear_gear[2];
	sprintf(Rear3_GadSIBuff,"%d",Rear3_GadSInfo.LongInt);
	Rear4_GadSInfo.LongInt = (long) rear_gear[3];
	sprintf(Rear4_GadSIBuff,"%d",Rear4_GadSInfo.LongInt);
	Rear5_GadSInfo.LongInt = (long) rear_gear[4];
	sprintf(Rear5_GadSIBuff,"%d",Rear5_GadSInfo.LongInt);
	Rear6_GadSInfo.LongInt = (long) rear_gear[5];
	sprintf(Rear6_GadSIBuff,"%d",Rear6_GadSInfo.LongInt);
	Rear7_GadSInfo.LongInt = (long) rear_gear[6];
	sprintf(Rear7_GadSIBuff,"%d",Rear7_GadSInfo.LongInt);

	/* read in colors */
	for (i=0;i<PaletteColorCount;i++) color[i] = -1;
	for (i=0;i<PaletteColorCount;i++)
		if (1 != fscanf(fp,",%d",&color[i])) goto skip;
	for (i=0;i<PaletteColorCount;i++) if (color[i] == -1) goto skip;
	for (i=0;i<PaletteColorCount;i++)  Palette[i] = (USHORT) color[i];
	LoadRGB4(&vP,&Palette,PaletteColorCount);
  skip:

	if (!check_gears())  return;   /* bad data check   */

	set_gads();
	set_menus();

	calc_it();         /* Calc the ratios */

	redraw_scr();      /* Update screen   */

	display_it();      /* Display the ratios */
}

/*
 *    Save the current data in an ASCII file
 */

do_save()
{
	FILE *fp;
	int i;

	if (!check_gears()) return;

	select_file(1);
	if (filename[0] == '\0') return;

	while ((fp = fopen(filename,"w")) == NULL)
		{
		infailtxt2.IText = (UBYTE *) filename;
		if (AutoRequest(wG,&infailtxt,&retrytxt,&cantxt,
			0L,0L,reqlen(filename),75L)) continue;
		return;
		}
	AddFileIOName(FIOSupp,filename); /* tell FIO about new file */

	fprintf(fp,"%d,%d,%d,",front,rear,Diameter_GadSInfo.LongInt);
	if (inmm_Gad.Flags & SELECTED ) fprintf(fp,"m");
	else fprintf(fp,"i");
	/* do gears */
	for (i=0;i<front;i++) fprintf(fp,",%2.0f",front_gear[i]);
	for (i=0;i<rear;i++) fprintf(fp,",%2.0f",rear_gear[i]);
	/* do colors */
	cm = Sc->ViewPort.ColorMap;
	if (cm) for (i = 0; i < 8; i++) fprintf(fp,",%d",GetRGB4(cm, (long) i));
	fprintf(fp,"\n");
	fclose(fp);

}


/*
 * Redraw Window
 */

redraw_scr()
{
	RemoveGList(wG,&GadgetList1,-1L); /* remove all gadgets */
	SetAPen(rpG,0L); /* clear the window */
	RectFill(rpG,2L,11L,(long)(wG->Width-3),(long)(wG->Height-2));
	AddGList(wG,&GadgetList1,0L,-1L,NULL);  /* put back gadgets */
	RefreshGList(&GadgetList1,wG,NULL,-1L); /* redraw the gadgets */
	PrintIText(rpG,&IntuiTextList1,0L,0L); /* write window text */
	SetAPen(rpG,7L);  /* set draw color */
}


/*
 * Select a file
 */

select_file(io)
int io;
{
	struct Window *OpenWindow(), *wF = NULL;
	struct FileLock *lock = NULL;

	ClearMenuStrip(wG);

	if (NULL == FIOSupp)
		if (NULL == (FIOSupp = GetFileIOSupport()))
			{
			AutoRequest(wG,&fiofailtxt,0L,&cantxt,0L,0L,300L,75L) ;
			SetMenuStrip(wG,&MenuList1);	/* re-attach my Menu */
			return;
			}

	/* set up title for file requester */
	if (io == 0) FIOSupp->ReqTitle = (UBYTE *) load_title;
	else FIOSupp->ReqTitle = (UBYTE *) save_title;

	wF = OpenWindow(&NewWindowStructure3);	/* open the window */
	if ( wF == NULL )
		{
		AutoRequest(wG,&winfailtxt,0L,&oktxt,0L,0L,300L,75L);
		SetMenuStrip(wG,&MenuList1);	/* re-attach my Menu */
		return;
		}

	filename[0] = '\0';
	if (GetFileIOName(FIOSupp,wF))
		BuildFileIOPathname(FIOSupp,filename);

	if (wF) CloseWindow(wF);
	if (lock) UnLock(lock);
	SetMenuStrip(wG,&MenuList1);	/* attach my Menu */
}


/*
 *  Calculate size of requester needed for given filename
 */

reqlen(filename)
char *filename;
{
	int r;
	r = TextLength(rpG,filename,strlen(filename))+50;
	if (r < 300) r = 300;
	if (r > 640) r = 640;
	return (r) ;
}
 
/*
 *
 *   Quick Sort routine. From Matt Dillon/Steve Drews Shell
 *
 */
 
QuickSort(av, n)
int av[];
int n;
{
   int b;
 
   if (n > 0) {
      b = QSplit(av, n);
      QuickSort(av, b);
      QuickSort(av+b+1, n - b - 1);
   }
}
 
 
/*
 * QSplit called as a second routine so I don't waste stack on QuickSort's
 * recursivness.
 */
 
QSplit(av, n)
int av[];
int n;
{
   int i, b;
   int element, scr;
 
   element = av[0];
   for (b = 0, i = 1; i < n; ++i) {
      if (ratios[av[i]] < ratios[element]) {
	     ++b;
	     scr = av[i]; av[i] = av[b]; av[b] = scr;
      }
   }
   scr = av[0]; av[0] = av[b]; av[b] = scr;
   return (b);
}
 
#ifdef AZTEC_C
_abort()
{
	done(13);
}
#endif
