#include <exec/types.h>
#include <exec/libraries.h>
#include <intuition/intuition.h>
#include <libraries/reqbase.h>
#include <libraries/ilbm_lib.h>
#include <workbench/startup.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define rad(x) ( (x) * (float)3.14159 / (float)180.0 )

/************************/
/* STRUCTURES		*/
/************************/

struct Memory
    {
	float x, y;
	float angle, size;
    };

struct LibEntry
    {
	struct LibEntry *Next;
	struct LibEntry *Prev;

	char Name[40];

	char Init[40];
	char Zero[40];
	char One[40];
	char Two[40];
	char Three[40];

	SHORT Size;
	SHORT Rate;
	SHORT AngleStart;
	SHORT AngleEnd;
	SHORT Generations;
    };

/************************/
/* PROTOTYPES		*/
/************************/

void main();
void wbmain();
void CloseAll(__A0 char *, __A1 char *);

void ClearWindow();
void DrawMainMenu();
void RedrawLibList();
void TreeGrow();

void CorrectString(__A0 char *);

void SingleGadgetRefresh(__A0 struct Gadget *, __A1 struct Window *);
void DrawFrame(__A0 struct RastPort *, __D0 SHORT, __D1 SHORT, __D2 SHORT, __D3 SHORT);
void PrintAt(__A0 struct RastPort *, __D0 SHORT, __D1 SHORT, __D2 SHORT, __A1 char *);
void ScrollPrint(__D0 SHORT, __D1 SHORT, __A0 char *);

/************************/
/* GLOBALS		*/
/************************/

/*************** Graphics ****************/

#include "TreeGrowGfx.h"

struct Image *Leaf[4] =
    {
	&Leaf1Image, &Leaf2Image, &Leaf3Image, &Leaf4Image
    };

struct Image *Red[4] =
    {
	&Red1Image, &Red2Image, &Red3Image, &Red4Image
    };

struct Image *Yellow[4] =
    {
	&Yellow1Image, &Yellow2Image, &Yellow3Image, &Yellow4Image
    };

USHORT ColorDef[] =
    {
	0x000, 0xb70, 0x960, 0x0a0, 0x080, 0xf20, 0x800, 0xfd0
    };

/*************** Gadgets ****************/

#include "TreeGrowGadgets.h"

/*************** FileRequester ****************/

char DirName[DSIZE+1];
char FileName[FCHARS+1];
char PathName[DSIZE+FCHARS+2];

struct ReqFileRequester ReqFileRequester =
    {
	REQVERSION,
	"Save IFF-Picture",
	DirName,
	FileName,
	PathName,

	NULL,

	0,
	10, 30, 20,
	FRQSHOWINFOM | FRQINFOGADGETM | FRQSAVINGM,
	3, 1, 1, 0, 0,
	0, 1,
	1, 1, 3, 3, 3, 3,

	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
	{ 0, 0, 0 },

	0, 0,
	0, 0,

	NULL,
	NULL,
	NULL,
	0, 0, 0, 0, 0, 0, 0, 0,

	NULL, NULL, NULL,
	NULL,
	NULL,
	0, 0, 0, 0
    };

/*************** ControlWindow ****************/

struct NewWindow ControlWindowDef =
    {
	155, 46, 365, 170,
	0, 1,
	GADGETUP | CLOSEWINDOW | MOUSEBUTTONS,
	WINDOWCLOSE | WINDOWDRAG | WINDOWDEPTH | SMART_REFRESH | NOCAREREFRESH | RMBTRAP | ACTIVATE,
	NULL,
	NULL,
	"TreeGrow V1.0",
	NULL,
	NULL,
	0, 0, 0, 0,
	WBENCHSCREEN
    };

struct Window *ControlWindow = NULL;
struct RastPort *conrp;

/*************** DrawWindow ****************/

struct NewWindow DrawWindowDef =
    {
	0, 0, 640, 512,
	0, 1,
	MOUSEBUTTONS,
	BORDERLESS | SMART_REFRESH | NOCAREREFRESH | RMBTRAP | ACTIVATE,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	0, 0, 0, 0,
	CUSTOMSCREEN
    };

struct Window *DrawWindow = NULL;
struct RastPort *drawrp;

/*************** DrawScreen ****************/

struct NewScreen DrawScreenDef =
    {
	0, 0, 640, 512,
	3,
	0, 1,
	HIRES | LACE,
	CUSTOMSCREEN,
	NULL,
	"TreeGrow V1.0",
	NULL,
	NULL
    };

struct Screen *DrawScreen = NULL;

/*************** Parameters ****************/

SHORT Size = 20;
SHORT Rate = 95;
SHORT AngleStart = 5;
SHORT AngleEnd = 20;
SHORT Generations = 3;

/*************** Misc ****************/

struct LibEntry *FirstLibEntry = NULL;
struct LibEntry *ActLibEntry = NULL;

struct Library *ILBMBase = NULL;
struct Library *ReqBase = NULL;

char Buffer[256];

/************************/
/* main()               */
/************************/

void wbmain(struct WBStartup *wbenchmsg)
    {
	CurrentDir(wbenchmsg->sm_ArgList->wa_Lock);

	main();
    }

void main()
    {
	/*************/
	/* open libs */
	/*************/

	ILBMBase = (struct Library *)OpenLibrary("ilbm.library", 0);
	if ( ILBMBase == NULL )
	    CloseAll("no ilbm.library", "Use InstallLibs !");

	ReqBase = (struct Library *)OpenLibrary("req.library", 0);
	if ( ReqBase == NULL )
	    CloseAll("no req.library", "Use InstallLibs !");

	/***********************/
	/* open control window */
	/***********************/

	ControlWindow = (struct Window *)OpenWindow(&ControlWindowDef);
	if ( ControlWindow == NULL )
	    CloseAll("can't open window", "Free some memory !");

	ReqFileRequester.Window = ControlWindow;

	conrp = ControlWindow->RPort;

	/****************/
	/* load library */
	/****************/
	    {
		FILE *file;

		file = fopen("TreeGrow.lib", "r");
		if ( file )
		    {
			struct LibEntry entry;
			struct LibEntry *new, *last = NULL;
			SHORT i;

			do  {
				i = fread(&entry, sizeof(struct LibEntry), 1, file);
				if ( i == 1 )
				    {
					new = malloc(sizeof(struct LibEntry));
					if ( new )
					    {
						memmove(new, &entry, sizeof(struct LibEntry));

						if ( last == NULL )
						    {
							FirstLibEntry = new;
							new->Prev = NULL;
						    }
						else
						    {
							last->Next = new;
							new->Prev = last;
						    }

						new->Next = NULL;
						last = new;
					    }
					else
					    {
						fclose(file);
						CloseAll("no memory for new library entry", "Free some memory !");
					    }
				    }
			    } while ( i == 1 );

			fclose(file);

			ActLibEntry = FirstLibEntry;
		    }
	    }

	DrawMainMenu();

	while ( TRUE )
	    {
		struct IntuiMessage *msg;

		Wait(1 << ControlWindow->UserPort->mp_SigBit );

		while ( msg = (struct IntuiMessage *)GetMsg(ControlWindow->UserPort) )
		    {
			ULONG class = msg->Class;
			struct Gadget *iaddr = msg->IAddress;

			ReplyMsg(msg);

			switch ( class )
			    {
				case CLOSEWINDOW :
				    CloseAll(NULL, NULL);

				case GADGETUP :
				    switch ( iaddr->GadgetID )
					{
					    case 1 :
						/********/
						/* Init */
						/********/

						CorrectString(Init_Buffer);
						if ( strlen(Init_Buffer) == 0 )
						    strcpy(Init_Buffer, "01");

						SingleGadgetRefresh(&Init_Gadget, ControlWindow);

						ActivateGadget(&Zero_Gadget, ControlWindow, NULL);

						break;

					    case 2 :
						/********/
						/* Zero */
						/********/

						CorrectString(Zero_Buffer);
						if ( strlen(Zero_Buffer) == 0 )
						    strcpy(Zero_Buffer, "0");

						SingleGadgetRefresh(&Zero_Gadget, ControlWindow);

						ActivateGadget(&One_Gadget, ControlWindow, NULL);

						break;

					    case 3 :
						/*******/
						/* One */
						/*******/

						CorrectString(One_Buffer);
						if ( strlen(One_Buffer) == 0 )
						    strcpy(One_Buffer, "1");

						SingleGadgetRefresh(&One_Gadget, ControlWindow);

						ActivateGadget(&Two_Gadget, ControlWindow, NULL);

						break;

					    case 4 :
						/*******/
						/* Two */
						/*******/

						CorrectString(Two_Buffer);
						if ( strlen(Two_Buffer) == 0 )
						    strcpy(Two_Buffer, "2");

						SingleGadgetRefresh(&Two_Gadget, ControlWindow);

						ActivateGadget(&Three_Gadget, ControlWindow, NULL);

						break;

					    case 5 :
						/*********/
						/* Three */
						/*********/

						CorrectString(Three_Buffer);
						if ( strlen(Three_Buffer) == 0 )
						    strcpy(Three_Buffer, "3");

						SingleGadgetRefresh(&Three_Gadget, ControlWindow);

						ActivateGadget(&Size_Gadget, ControlWindow, NULL);

						break;

					    case 6 :
						/********/
						/* Size */
						/********/

						Size = atoi(Size_Buffer);

						if ( Size < 5 )
						    Size = 5;
						else if ( Size > 100 )
						    Size = 100;

						sprintf(Size_Buffer, "%u", Size);

						SingleGadgetRefresh(&Size_Gadget, ControlWindow);

						ActivateGadget(&Rate_Gadget, ControlWindow, NULL);

						break;

					    case 18 :
						/********/
						/* rate */
						/********/

						Rate = atoi(Rate_Buffer);

						if ( Rate < 1 )
						    Rate = 1;
						else if ( Rate > 100 )
						    Rate = 100;

						sprintf(Rate_Buffer, "%u", Rate);

						SingleGadgetRefresh(&Rate_Gadget, ControlWindow);

						ActivateGadget(&AngleStart_Gadget, ControlWindow, NULL);

						break;

					    case 7 :
						/**************/
						/* AngleStart */
						/**************/

						AngleStart = atoi(AngleStart_Buffer);

						if ( AngleStart < -180 )
						    AngleStart = -180;
						else if ( AngleStart > 180 )
						    AngleStart = 180;

						if ( AngleStart >= AngleEnd)
						    {
							AngleEnd = AngleStart + 10;
							sprintf(AngleEnd_Buffer, "%d", AngleEnd);

							SingleGadgetRefresh(&AngleEnd_Gadget, ControlWindow);
						    }

						sprintf(AngleStart_Buffer, "%d", AngleStart);

						SingleGadgetRefresh(&AngleStart_Gadget, ControlWindow);

						ActivateGadget(&AngleEnd_Gadget, ControlWindow, NULL);

						break;

					    case 8 :
						/************/
						/* AngleEnd */
						/************/

						AngleEnd = atoi(AngleEnd_Buffer);

						if ( AngleEnd < -180 )
						    AngleEnd = -180;
						else if ( AngleEnd > 180 )
						    AngleEnd = 180;

						if ( AngleStart >= AngleEnd)
						    {
							AngleStart = AngleEnd - 10;
							sprintf(AngleStart_Buffer, "%d", AngleStart);

							SingleGadgetRefresh(&AngleStart_Gadget, ControlWindow);
						    }

						sprintf(AngleEnd_Buffer, "%d", AngleEnd);

						SingleGadgetRefresh(&AngleEnd_Gadget, ControlWindow);

						ActivateGadget(&Generations_Gadget, ControlWindow, NULL);

						break;

					    case 9 :
						/***************/
						/* Generations */
						/***************/

						Generations = atoi(Generations_Buffer);

						if ( Generations < 1 )
						    Generations = 1;
						else if ( Generations > 50 )
						    Generations = 50;

						sprintf(Generations_Buffer, "%u", Generations);

						SingleGadgetRefresh(&Generations_Gadget, ControlWindow);

						ActivateGadget(&Name_Gadget, ControlWindow, NULL);

						break;

					    case 10 :
						/********/
						/* Name */
						/********/

						ActivateGadget(&Init_Gadget, ControlWindow, NULL);

						break;

					    case 11 :
						/********/
						/* Save */
						/********/
						    {
							FILE *file;

							file = fopen("TreeGrow.lib", "w");
							if ( file )
							    {
								struct LibEntry *scan;

								for ( scan = FirstLibEntry ; scan ; scan = scan->Next )
								    fwrite(scan, sizeof(struct LibEntry), 1, file);

								fclose(file);
							    }
							else
							    {
								SetWindowTitles(ControlWindow, "Can't open file.", -1);
								DisplayBeep(NULL);
							    }

							SetWindowTitles(ControlWindow, "Library saved.", -1);
						    }
						break;

					    case 12 :
						/*******/
						/* Add */
						/*******/
						    {
							struct LibEntry *new, *scan;

							scan = FirstLibEntry;

							while ( stricmp(scan->Name, Name_Buffer) && scan)
							    scan = scan->Next;

							if ( scan )
							    {
								strcpy(scan->Name, Name_Buffer);
								strcpy(scan->Init, Init_Buffer);
								strcpy(scan->Zero, Zero_Buffer);
								strcpy(scan->One, One_Buffer);
								strcpy(scan->Two, Two_Buffer);
								strcpy(scan->Three, Three_Buffer);
								scan->Size = Size;
								scan->Rate = Rate;
								scan->AngleStart = AngleStart;
								scan->AngleEnd = AngleEnd;
								scan->Generations = Generations;

								ActLibEntry = scan;
								RedrawLibList();
							    }
							else
							    {
								new = malloc(sizeof(struct LibEntry));
								if ( new )
								    {
									strcpy(new->Name, Name_Buffer);
									strcpy(new->Init, Init_Buffer);
									strcpy(new->Zero, Zero_Buffer);
									strcpy(new->One, One_Buffer);
									strcpy(new->Two, Two_Buffer);
									strcpy(new->Three, Three_Buffer);
									new->Size = Size;
									new->Rate = Rate;
									new->AngleStart = AngleStart;
									new->AngleEnd = AngleEnd;
									new->Generations = Generations;

									if ( FirstLibEntry == NULL )
									    {
										FirstLibEntry = new;

										new->Prev = NULL;
										new->Next = NULL;
									    }
									else
									    {
										scan = FirstLibEntry;

										while ( (stricmp(scan->Name, Name_Buffer) < 0) && scan )
										    scan = scan->Next;

										if ( scan == NULL )
										    {
											scan = FirstLibEntry;

											while ( scan->Next )
											    scan = scan->Next;

											scan->Next = new;
											new->Prev = scan;
											new->Next = NULL;
										    }
										else if ( scan == FirstLibEntry )
										    {
											new->Next = scan;
											new->Prev = NULL;
											FirstLibEntry = new;
											scan->Prev = new;
										    }
										else
										    {
											scan->Prev->Next = new;
											new->Prev = scan->Prev;
											new->Next = scan;
											scan->Prev = new;
										    }
									    }

									ActLibEntry = new;
									RedrawLibList();
								    }
								else
								    {
									SetWindowTitles(ControlWindow, "No memory for new entry.", -1);
									DisplayBeep(NULL);
								    }
							    }
						    }

						break;

					    case 13 :
						/*******/
						/* Del */
						/*******/

						if ( ActLibEntry )
						    {
							struct LibEntry *entry = ActLibEntry;

							ActLibEntry = entry->Prev;
							if ( ActLibEntry == NULL )
							    ActLibEntry = entry->Next;

							if ( entry->Next )
							    entry->Next->Prev = entry->Prev;

							if ( entry->Prev )
							    entry->Prev->Next = entry->Next;
							else
							    {
								FirstLibEntry = entry->Next;
								if ( FirstLibEntry )
								    FirstLibEntry->Prev = NULL;
							    }

							free(entry);

							RedrawLibList();
						    }
						break;

					    case 14 :
						/*******/
						/* Use */
						/*******/

						if ( ActLibEntry )
						    {
							strcpy(Name_Buffer, ActLibEntry->Name);
							strcpy(Init_Buffer, ActLibEntry->Init);
							strcpy(Zero_Buffer, ActLibEntry->Zero);
							strcpy(One_Buffer, ActLibEntry->One);
							strcpy(Two_Buffer, ActLibEntry->Two);
							strcpy(Three_Buffer, ActLibEntry->Three);

							Size = ActLibEntry->Size;
							Rate = ActLibEntry->Rate;
							AngleStart = ActLibEntry->AngleStart;
							AngleEnd = ActLibEntry->AngleEnd;
							Generations = ActLibEntry->Generations;

							sprintf(Size_Buffer, "%u", Size);
							sprintf(Rate_Buffer, "%u", Rate);
							sprintf(AngleStart_Buffer, "%d", AngleStart);
							sprintf(AngleEnd_Buffer, "%d", AngleEnd);
							sprintf(Generations_Buffer, "%u", Generations);

							RefreshGadgets(&Init_Gadget, ControlWindow, NULL);
						    }
						break;

					    case 15 :
						/******/
						/* Up */
						/******/

						if ( ActLibEntry->Prev )
						    ActLibEntry = ActLibEntry->Prev;

						RedrawLibList();
						break;

					    case 16 :
						/********/
						/* Down */
						/********/

						if ( ActLibEntry->Next )
						    ActLibEntry = ActLibEntry->Next;

						RedrawLibList();
						break;

					    case 17 :
						/*********/
						/* Start */
						/*********/
						    {
							BOOL bool = TRUE;

							ClearWindow();

							TreeGrow();

							ScrollPrint(2, 10, "Press mousebutton to continue.");

							while ( bool )
							    {
								Wait(1 << ControlWindow->UserPort->mp_SigBit );

								while ( msg = (struct IntuiMessage *)GetMsg(ControlWindow->UserPort) )
								    {
									if ( (msg->Class == MOUSEBUTTONS) && (msg->Code == SELECTUP) )
									    bool = FALSE;

									ReplyMsg(msg);
								    }
							    }

							DrawMainMenu();
						    }
						break;

					    case 19 :
						/********/
						/* Save */
						/********/

						if ( DrawWindow )
						    {
							if ( FileRequester(&ReqFileRequester) )
							    {
								ScreenToFront(DrawScreen);

								if ( SaveWindowToIFF(PathName, DrawWindow) < 0 )
								    {
									SetWindowTitles(ControlWindow, "Error writing file.", -1);
									DisplayBeep(NULL);
								    }
								else
								    {
									SetWindowTitles(ControlWindow, "Picture saved.", -1);
									DisplayBeep(NULL);
								    }

								WBenchToFront();
							    }
						    }
						else
						    {
							SetWindowTitles(ControlWindow, "There's no tree to save..", -1);
							DisplayBeep(NULL);
						    }
						break;
					}
				    break;
			    }
		    }
	    }
    }

/************************/
/* CloseAll()           */
/************************/

void CloseAll(__A0 char *text1, __A1 char *text2)
    {
	if ( text1 )
	    {
		char String[256];
		SHORT x1, x2, i, j;

		sprintf(String, "xxyTreeGrow says : %s0Txxy%s0F", text1, text2);
		x1 = 320 - ( (strlen(text1) + 16) << 2 );
		x2 = 320 - ( strlen(text2)  << 2 );
		i = 19 + strlen(text1);
		j = i + 5 + strlen(text2);

		String[0] = (char)(x1 >> 8);
		String[1] = (char)(x1 & 0xFF);
		String[2] = 23;
		String[i] = 0;
		String[i+1] = TRUE;
		String[i+2] = (char)(x2 >> 8);
		String[i+3] = (char)(x2 & 0xFF);
		String[i+4] = 31;
		String[j] = 0;
		String[j] = FALSE;

		DisplayAlert(RECOVERY_ALERT, String, 50);
	    }

	if ( DrawWindow ) CloseWindow(DrawWindow);
	if ( DrawScreen ) CloseScreen(DrawScreen);
	if ( ControlWindow ) CloseWindow(ControlWindow);

	if ( ILBMBase ) CloseLibrary(ILBMBase);
	if ( ReqBase ) CloseLibrary(ReqBase);

	/* free library */
	    {
		struct LibEntry *scan, *next;

		for ( scan = FirstLibEntry ; scan ; scan = next )
		    {
			next = scan->Next;

			free(scan);
		    }
	    }

	exit(0);
    }

/************************/
/* ClearWindow()        */
/************************/

void ClearWindow()
    {
	ControlWindow->FirstGadget = Start_Gadget.NextGadget;
	Start_Gadget.NextGadget = NULL;

	SetAPen(conrp, 0);
	RectFill(conrp, 2, 10, 362, 168);
    }

/************************/
/* DrawMainMenu()       */
/************************/

void DrawMainMenu()
    {
	SetAPen(conrp, 0);
	RectFill(conrp, 2, 10, 362, 168);

	Start_Gadget.NextGadget = ControlWindow->FirstGadget;
	ControlWindow->FirstGadget = &Init_Gadget;

	RefreshGadgets(&Init_Gadget, ControlWindow, NULL);

	DrawFrame(conrp, 70, 11, 277, 22); /*init*/
	DrawFrame(conrp, 46, 23, 277, 34); /*zero*/
	DrawFrame(conrp, 46, 35, 277, 46); /*one*/
	DrawFrame(conrp, 46, 47, 277, 58); /*two*/
	DrawFrame(conrp, 46, 59, 277, 70); /*three*/
	DrawFrame(conrp, 117, 71, 164, 82); /*size*/
	DrawFrame(conrp, 230, 71, 277, 82); /*rate*/
	DrawFrame(conrp, 117, 83, 164, 94); /*anglestart*/
	DrawFrame(conrp, 187, 83, 234, 94); /*angleend*/
	DrawFrame(conrp, 117, 95, 164, 106); /*generations*/
	DrawFrame(conrp, 62, 112, 277, 123); /*name*/
	DrawFrame(conrp, 10, 124, 73, 135); /*save*/
	DrawFrame(conrp, 80, 124, 143, 135); /*add*/
	DrawFrame(conrp, 150, 124, 213, 135); /*del*/
	DrawFrame(conrp, 220, 124, 283, 135); /*use*/
	DrawFrame(conrp, 10, 140, 25, 152); /*up*/
	DrawFrame(conrp, 10, 153, 25, 165); /*down*/
	DrawFrame(conrp, 280, 52, 359, 91); /*save*/
	DrawFrame(conrp, 280, 11, 359, 50); /*start*/

	SetAPen(conrp, 1);
	PrintAt(conrp, 70, 19, 1, "Init : ");
	PrintAt(conrp, 46, 31, 1, "0 -> ");
	PrintAt(conrp, 46, 43, 1, "1 -> ");
	PrintAt(conrp, 46, 55, 1, "2 -> ");
	PrintAt(conrp, 46, 67, 1, "3 -> ");
	PrintAt(conrp, 117, 79, 1, "Size........: ");
	PrintAt(conrp, 230, 79, 1, "Rate : ");
	PrintAt(conrp, 117, 91, 1, "Angle.......: ");
	PrintAt(conrp, 175, 91, 0, "to");
	PrintAt(conrp, 117, 103, 1, "Generations.: ");
	PrintAt(conrp, 62, 120, 1, "Name : ");
	PrintAt(conrp, 42, 132, 0, "Save");
	PrintAt(conrp, 112, 132, 0, "Add");
	PrintAt(conrp, 182, 132, 0, "Del");
	PrintAt(conrp, 252, 132, 0, "Use");
	PrintAt(conrp, 320, 74, 0, "Save");
	PrintAt(conrp, 320, 33, 0, "Start");

	Move(conrp, 2, 109);
	Draw(conrp, 362, 109);

	RedrawLibList();
    };

/************************/
/* RedrawLibList()      */
/************************/

void RedrawLibList()
    {
	SetAPen(conrp, 0);
	RectFill(conrp, 30, 141, 362, 164);

	if ( ActLibEntry )
	    {
		SetAPen(conrp, 1);
		PrintAt(conrp, 30, 155, -1, ActLibEntry->Name);

		SetAPen(conrp, 2);
		if ( ActLibEntry->Prev )
		    PrintAt(conrp, 30, 147, -1, ActLibEntry->Prev->Name);

		if ( ActLibEntry->Next )
		    PrintAt(conrp, 30, 163, -1, ActLibEntry->Next->Name);
	    }
    }

/************************/
/* CorrectString()      */
/************************/

void CorrectString(__A0 char *string)
    {
	char *s = string;
	SHORT i = 0;

	while ( *s )
	    {
		switch ( *s )
		    {
			case '0' :
			case '1' :
			case '2' :
			case '3' :
			case '\\' :
			case '/' :
			case '[' :
			case ']' :
			    Buffer[i] = *s;
			    i++;
			    break;
		    }

		s++;
	    }

	Buffer[i] = 0;

	strcpy(string, Buffer);
    }

/*************************/
/* SingleGadgetRefresh() */
/*************************/

void SingleGadgetRefresh(__A0 struct Gadget *gad, __A1 struct Window *win)
    {
	struct Gadget *next;

	next = gad->NextGadget;
	gad->NextGadget = NULL;

	RefreshGadgets(gad, win, NULL);

	gad->NextGadget = next;
    }

/************************/
/* DrawFrame()          */
/************************/

void DrawFrame(__A0 struct RastPort *rp, __D0 SHORT x1, __D1 SHORT y1, __D2 SHORT x2, __D3 SHORT y2)
    {
	SetAPen(rp, 1);
	Move(rp, x1, y1);
	Draw(rp, x1, y2);
	Draw(rp, x1+1, y2-1);
	Draw(rp, x1+1, y1);
	Draw(rp, x2-1, y1);

	SetAPen(rp, 2);
	Move(rp, x2, y2);
	Draw(rp, x2, y1);
	Draw(rp, x2-1, y1+1);
	Draw(rp, x2-1, y2);
	Draw(rp, x1+1, y2);
    }

/************************/
/* PrintAt()            */
/************************/

void PrintAt(__A0 struct RastPort *rp, __D0 SHORT x, __D1 SHORT y, __D2 SHORT m, __A1 char *text)
    {
	SHORT l = strlen(text);
	SHORT tl = TextLength(rp, text, l);

	switch ( m )
	    {
		case 0 :
		    x -= tl/2;
		    break;
		case 1 :
		    x -= tl;
		    break;
	    }

	Move(rp, x, y);
	Text(rp, text, l);
    }

/************************/
/* ScrollPrint()        */
/************************/

void ScrollPrint(__D0 SHORT c, __D1 SHORT x, __A0 char *text)
    {
	ScrollRaster(conrp, 0, 8, 2, 10, 327, 168);

	SetAPen(conrp, c);
	Move(conrp, x, 160);
	Text(conrp, text, strlen(text));
    }

/************************/
/* TreeGrow()           */
/************************/

void TreeGrow()
    {
	char *source;
	struct Memory *mem;

	/****************/
	/* alloc memory */
	/****************/
	    {
		mem = malloc(5000 * sizeof(struct Memory));
		if ( mem == NULL )
		    {
			ScrollPrint(3, 10, "Ran out of memory.");
			return();
		    }
	    }

	/*****************/
	/* generate tree */
	/*****************/
	    {
		char *dest;
		SHORT gen = 1;
		LONG size;
		LONG zero = strlen(Zero_Buffer);
		LONG one = strlen(One_Buffer);
		LONG two = strlen(Two_Buffer);
		LONG three = strlen(Three_Buffer);

		source = malloc(strlen(Init_Buffer)+1);
		if ( source == NULL )
		    {
			ScrollPrint(3, 10, "Ran out of memory.");
			return();
		    }

		strcpy(source, Init_Buffer);

		while ( gen <= Generations )
		    {
			char *scan, *next;

			size = 0;

			for ( scan = source ; *scan ; scan++ )
			    switch ( *scan )
				{
				    case '0' :
					size += zero;
					break;
				    case '1' :
					size += one;
					break;
				    case '2' :
					size += two;
					break;
				    case '3' :
					size += three;
					break;
				    case '\\' :
				    case '/' :
				    case '[' :
				    case ']' :
					size++;
					break;
				    default :
					ScrollPrint(3, 10, "Error in source string.");
					return();
				}

			sprintf(Buffer, "% 2u. Generation : % 7u chars", gen, size);
			ScrollPrint(1, 10, Buffer);

			dest = malloc(size + 1);
			if ( dest == NULL )
			    {
				ScrollPrint(3, 10, "Ran out of memory.");
				return();
			    }

			next = dest;

			for ( scan = source ; *scan ; scan++ )
			    switch ( *scan )
				{
				    case '0' :
					strcpy(next, Zero_Buffer);
					next += zero;
					break;
				    case '1' :
					strcpy(next, One_Buffer);
					next += one;
					break;
				    case '2' :
					strcpy(next, Two_Buffer);
					next += two;
					break;
				    case '3' :
					strcpy(next, Three_Buffer);
					next += three;
					break;
				    case '\\' :
					*next = '\\';
					next++;
					break;
				    case '/' :
					*next = '/';
					next++;
					break;
				    case '[' :
					*next = '[';
					next++;
					break;
				    case ']' :
					*next = ']';
					next++;
					break;
				    default :
					ScrollPrint(3, 10, "Error in source string.");
					return();
				}

			*next = 0;

			free(source);
			source = dest;
			dest = NULL;

			gen++;
		    }

		ScrollPrint(1, 10, "Generation completed.");
	    }

	/*************/
	/* draw tree */
	/*************/
	    {
		BOOL bool = TRUE;
		SHORT actmem = -1;
		SHORT AngleDiff = AngleEnd - AngleStart;

		float x = 320, y = 510;
		float angle = 90;
		float size = (float)Size;
		float rate = (float)Rate/100;
		char *scan;

		ScrollPrint(1, 10, "Drawing..");

		if ( DrawWindow ) CloseWindow(DrawWindow);
		if ( DrawScreen ) CloseScreen(DrawScreen);
		DrawWindow = NULL;
		DrawScreen = NULL;

		DrawScreen = (struct Screen *)OpenScreen(&DrawScreenDef);
		if ( DrawScreen == NULL )
		    {
			ScrollPrint(3, 10, "Can't open screen.");
			return();
		    }

		LoadRGB4(&DrawScreen->ViewPort, ColorDef, 8);

		DrawWindowDef.Screen = DrawScreen;

		DrawWindow = (struct Window *)OpenWindow(&DrawWindowDef);
		if ( DrawWindow == NULL )
		    {
			CloseScreen(DrawScreen);
			DrawScreen = NULL;

			ScrollPrint(3, 10, "Can't open window.");
			return();
		    }

		drawrp = DrawWindow->RPort;
		Move(drawrp, (int)x, (int)y);

		for ( scan = source ; *scan && bool ; scan++ )
		    {
			struct IntuiMessage *msg;

			while ( msg = (struct IntuiMessage *)GetMsg(DrawWindow->UserPort) )
			    {
				if ( (msg->Class == MOUSEBUTTONS) && (msg->Code == MENUUP) )
				    bool = FALSE;

				ReplyMsg(msg);
			    }

			switch ( *scan )
			    {
				case '0' :
				    x += fcos(rad(angle)) * size;
				    y -= fsin(rad(angle)) * size;
				    size *= rate;

				    SetAPen(drawrp, (rand() & 1)+1);
				    Draw(drawrp, (int)x, (int)y);
				    break;

				case '1' :
				    x += fcos(rad(angle)) * size;
				    y -= fsin(rad(angle)) * size;
				    size *= rate;

				    SetAPen(drawrp, (rand() & 1)+1);
				    Draw(drawrp, (int)x, (int)y);

				    DrawImage(drawrp, Leaf[rand() & 3], (int)x, (int)y);
				    break;

				case '2' :
				    x += fcos(rad(angle)) * size;
				    y -= fsin(rad(angle)) * size;
				    size *= rate;

				    SetAPen(drawrp, (rand() & 1)+1);
				    Draw(drawrp, (int)x, (int)y);

				    DrawImage(drawrp, Red[rand() & 3], (int)x, (int)y);
				    break;

				case '3' :
				    x += fcos(rad(angle)) * size;
				    y -= fsin(rad(angle)) * size;
				    size *= rate;

				    SetAPen(drawrp, (rand() & 1)+1);
				    Draw(drawrp, (int)x, (int)y);

				    DrawImage(drawrp, Yellow[rand() & 3], (int)x, (int)y);
				    break;

				case '\\' :
				    {
					actmem++;

					if ( actmem >= 5000 )
					    {
						CloseWindow(DrawWindow);
						CloseScreen(DrawScreen);
						DrawWindow = NULL;
						DrawScreen = NULL;

						ScrollPrint(3, 10, "More than 5000 branches.");
						return();
					    }

					struct Memory *m = mem + actmem*sizeof(struct Memory);
					m->x = x;
					m->y = y;
					m->angle = angle;
					m->size = size;

					angle += (float)( AngleStart + (rand() % AngleDiff) );
				    }
				    break;

				case '/' :
				    {
					actmem++;

					if ( actmem >= 5000 )
					    {
						CloseWindow(DrawWindow);
						CloseScreen(DrawScreen);
						DrawWindow = NULL;
						DrawScreen = NULL;

						ScrollPrint(3, 10, "More than 5000 branches.");
						return();
					    }

					struct Memory *m = mem + actmem*sizeof(struct Memory);
					m->x = x;
					m->y = y;
					m->angle = angle;
					m->size = size;

					angle -= (float)( AngleStart + (rand() % AngleDiff) );
				    }
				    break;

				case '[' :
				    {
					actmem++;

					if ( actmem >= 5000 )
					    {
						CloseWindow(DrawWindow);
						CloseScreen(DrawScreen);
						DrawWindow = NULL;
						DrawScreen = NULL;

						ScrollPrint(3, 10, "More than 5000 branches.");
						return();
					    }

					struct Memory *m = mem + actmem*sizeof(struct Memory);
					m->x = x;
					m->y = y;
					m->angle = angle;
					m->size = size;

					if ( rand() & 1 )
					    angle -= (float)( AngleStart + (rand() % AngleDiff) );
					else
					    angle += (float)( AngleStart + (rand() % AngleDiff) );
				    }
				    break;

				case ']' :
				    {
					if ( actmem < 0 )
					    {
						CloseWindow(DrawWindow);
						CloseScreen(DrawScreen);
						DrawWindow = NULL;
						DrawScreen = NULL;

						ScrollPrint(3, 10, "']' without '\\' or '/' occured.");
						return();
					    }

					struct Memory *m = mem + actmem*sizeof(struct Memory);
					x = m->x;
					y = m->y;
					angle = m->angle;
					size = m->size;

					Move(drawrp, (int)x, (int)y);

					actmem--;
				    }
				    break;

				default :
				    CloseWindow(DrawWindow);
				    CloseScreen(DrawScreen);
				    DrawWindow = NULL;
				    DrawScreen = NULL;

				    ScrollPrint(3, 10, "Error in source string");
				    return();
			    }
		    }
	    }

	/**********/
	/* finish */
	/**********/
	    {
		BOOL bool = TRUE;
		struct IntuiMessage *msg;

		free(source);
		free(mem);

		DisplayBeep(NULL);
		SetAPen(drawrp, 1);
		PrintAt(drawrp, 0, 6, -1, "Press LMB");

		while ( msg = (struct IntuiMessage *)GetMsg(DrawWindow->UserPort) )
		    ReplyMsg(msg);

		while ( bool )
		    {
			Wait(1 << DrawWindow->UserPort->mp_SigBit );

			while ( msg = (struct IntuiMessage *)GetMsg(DrawWindow->UserPort) )
			    {
				if ( (msg->Class == MOUSEBUTTONS) && (msg->Code == SELECTUP) )
				    bool = FALSE;

				ReplyMsg(msg);
			    }
		    }

		WBenchToFront();
	    }
    }

