/*-- Rev Header - do NOT edit!
 *
 *  Filename : DemoSource.cc
 *  Purpose  : Shows some features of my provided Classes etc.
 *
 *  Program  : TestExit
 *  Author   : Gerhard Müller
 *  Copyright: (c) by Gerhard Müller
 *  Creation : Sun Sep 12 00:17:36 1993
 *
 *  compile  : makefile
 *
 *
 *  Compile version  : 0.1
 *  Ext. Version     : 0.1
 *
 *  REVISION HISTORY
 *
 *  Date       C-Version  E-Version    Comment
 *  ---------  ---------  -----------  -------
 *  12.09.93                           work started
 *  25.10.93                           added a better new()
 *
 *
 *-- REV_END --
 */

	/*
	 * C-Includes, C-Definitionen
	 *
	 */

#define class _class
#define template _template

extern "C" {
#include <exec/types.h>
#include <exec/ports.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/exall.h>
#include <dos/rdargs.h>
#include <clib/alib_protos.h>
#include <clib/alib_stdio_protos.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfxbase.h>
#include <inline/stubs.h>
#include <clib/dos_protos.h>
#ifdef __OPTIMIZE__
#include <inline/exec.h>
#include <inline/dos.h>
#include <inline/intuition.h>
#else
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#endif

int	 atexit(void (*)(void));
}

#undef template
#undef class


extern "C" {

/* dont mangle our global symbols */

IntuitionBase	*IntuitionBase;
GfxBase			*GfxBase;
Library			*GadToolsBase;
Library			*UtilityBase;
Library			*WorkbenchBase;
Library			*IconBase;
Library			*CxBase;
Library			*IFFParseBase;
Library			*AslBase;

extern ExecBase	*SysBase;

extern BPTR stdin;
extern BPTR stdout;
}

BPTR stdin_WB = FALSE;


	/*
	 * C++-Includes, C++-Definitionen
	 *
	 */

#include "add/baserel/add.h"		// prototypes for all new C-functions
#include "add/baserel/couta.h"		// defines cout
#include "add/baserel/cina.h"		// defines cin
#include "add/baserel/OwnError.h"	// defines err
#include "add/baserel/Editfile.h"	// makes it easier to read in a file
#include "add/baserel/CED_Support.h"// allow communication with CED 3.5+



	/*
	 * our function-prototypes
	 *
	 */

void	OutputError(void);
void	MyExit();
BOOL	MyInit(int alen, char *aptr);
int		main(int alen, char *aptr);
void	OutputError(void);
BOOL	ask_user_to_free_memory(ULONG len, struct Window *win=NULL);


/* Version-String */

char *version = "\0$VER: Example 0.02 (" __DATE__ ")";



/* things for commandline-parsing */

#define TEMPLATE "STRING/A,INTEGER/N,TEST"

typedef struct { char *string; long *integer; char *test; } ARGS;
#define DEFAULT { NULL, &integer, "test" }

long		 	integer	=	99L;

ARGS			args = DEFAULT;	/* here the arguments are stored */

struct RDArgs	*rda = NULL;	/* for CLI-startup */
UBYTE			**ToolTypes; 	/* for WB-startup */




	/*
	 * void *operator new(unsigned long int anz)
	 *
	 * This overloads the standard new operator with a friendlier one.
	 * It won't terminate the program, just give zero as return if
	 * no memory is available, and asks the user to free memory.
	 *
	 * The user of new HAS to check if the variable is available!!
	 *
	 */



void *operator new(unsigned long int anz)
{
	char *ptr=NULL;

	while(1)
	{
		if(ptr=(char *)AllocVec(anz, 0))
			return (ptr);
		else
		{
			if(!IntuitionBase) return NULL;
			if(!ask_user_to_free_memory(anz)) return NULL;
		}
	}
}


	/* this one is the same as above, but you can define which
       kind of memory you need (MEMF_CHIP, MEMF_FAST, MEMF_CLEAR etc. */

void *operator new(unsigned long int anz, ULONG requirements)
{
	char *ptr=NULL;

	while(1)
	{
		if(ptr=(char *)AllocVec(anz, requirements))
			return (ptr);
		else
		{
			if(!IntuitionBase) return NULL;
			if(!ask_user_to_free_memory(anz)) return NULL;
		}
	}
}

void operator delete(void *mem)
{
	FreeVec(mem);
}


	/*
	 * Dummy Class, provided as an example only, has no real use
	 *
	 */

class xxxx {
 public:
	xxxx(ULONG mem_len=10000) : mem(0)			// initialise mem to 0, not necessary in our class, but...
	{
		// show where we are called
		cout << "xxxx Constructor: file: " << __FILE__ << "  line: "<< __LINE__ << endl;

		mem=new (MEMF_CLEAR) char[mem_len];
	}

	~xxxx()
	{
		// show where we are called
		cout << "xxxx Destructor: file: " << __FILE__ << "  line: "<< __LINE__ << endl;

		delete [] mem;
	}

	char *getmem(){return mem;};

private:
	char *mem;

};




/* Code fragment for use within new */

/* Asks the user to free memory */

BOOL ask_user_to_free_memory(ULONG len, struct Window *win=NULL)
{
	BOOL return_ok			= FALSE;

	// Request User

	UBYTE ProgName[50];

	GetProgramName(ProgName,49);

	return_ok=ShowRequest(win, (UBYTE *)"We have memory trouble...",
		(UBYTE *)"Program\n   %s\nneeds %ld Bytes of memory.\nSo much bytes are not free.\nPlease try to free some memory or give up\n",
		(UBYTE *)"Try again|Give up",  ProgName, len);

	return return_ok;
}


	/*
	 * This function gets called by exit(), look im MyInit() for corresponding atexit() call
	 * frees all system-depended things
	 */


void MyExit()
{
	cout << "MyExit() is called. file: " << __FILE__ << "  line: "<< __LINE__ <<endl;


		/* Close WB-Window stream */

	if( stdin_WB )
	{
		stdin=0;			// set all things back to normal
		stdout=0;			// just for security reasons
		SelectInput(0);
		SelectOutput(0);

		cin.attach(0);		// just for security, not really necessary
		cout.attach(0);

		Close(stdin_WB);	// the window now can be closed (forever) by the user via CTRL-\ or via close-gadget
	}



		/* free WB-arguments */

	if( ToolTypes )
	{
		ArgArrayDone();
	}


		/* Free arguments passed by commandline allocated by ReadArgs() */

	if( rda )
	{
		FreeArgs( rda );
		FreeDosObject( DOS_RDARGS, rda );
	}


		/* close all our libraries */

		/* Close asl.library. */

	if(AslBase)
		CloseLibrary(AslBase);

		/* Close iffparse.library. */

	if(IFFParseBase)
		CloseLibrary(IFFParseBase);

		/* Close commodities.library. */

	if(CxBase)
		CloseLibrary(CxBase);

		/* Close icon.library. */

	if(IconBase)
		CloseLibrary(IconBase);

		/* Close workbench.library. */

	if(WorkbenchBase)
		CloseLibrary(WorkbenchBase);

		/* Close utility.library. */

	if(UtilityBase)
		CloseLibrary(UtilityBase);

		/* Close gadtools.library. */

	if(GadToolsBase)
		CloseLibrary(GadToolsBase);

		/* Close graphics.library. */

	if(GfxBase)
	{
		// This construction gives the compiler to distinguish between
		// the struct GfxBase {/*...*/};  and the variable GfxBase. If this
		// is not done, the warning (and following error)
		//
		//	warning: name `IntuitionBase' could be type or expression; compiler assuming type
		//	parse error before `;'
		//
		// were the consequence.

		struct Library *dummy=(struct Library *)GfxBase;
		CloseLibrary(dummy);
	}

		/* Close intuition.library. */

	if(IntuitionBase)
	{
		struct Library *dummy=(struct Library *)IntuitionBase;
		CloseLibrary(dummy);
	}

}


	/*
	 * Open some libraries and make the commandline-parsing
	 *
	 */

BOOL MyInit(int alen, char *aptr)
{
	BOOL ok=FALSE;

	/* Are we running under control of Kickstart 2.0 or higher? */

	if(SysBase -> LibNode . lib_Version < 37)
	{
		err.NotifyError("needs Workbench & Kickstart Version 2.04 and above !\n");
		return FALSE;
	}


	atexit(MyExit);			// the function MyExit should be called at exit()-time


	/* open some libraries, not really needed in this programm, just to show how to do */

	if(IntuitionBase = (struct IntuitionBase *)OpenLibrary((UBYTE *)"intuition.library",37))
	{
		if(GfxBase = (struct GfxBase *)OpenLibrary((UBYTE *)"graphics.library",37))
		{
			if(GadToolsBase = OpenLibrary((UBYTE *)"gadtools.library",37))
			{
				if(UtilityBase = OpenLibrary((UBYTE *)"utility.library",37))
				{
					if(WorkbenchBase = OpenLibrary((UBYTE *)"workbench.library",37))
					{
						if(IconBase = OpenLibrary((UBYTE *)"icon.library",37))
						{
							if(CxBase = OpenLibrary((UBYTE *)"commodities.library",37))
							{
								if(IFFParseBase = OpenLibrary((UBYTE *)"iffparse.library",37))
								{
									if(AslBase = OpenLibrary((UBYTE *)"asl.library",37))
									{
										// we have all our libraries
										ok=TRUE;
									}
									else err.NotifyError("unable to open asl.library !");
								}
								else err.NotifyError("unable to open iffparse.library !");
							}
							else err.NotifyError("unable to open commodities.library !");
						}
						else err.NotifyError("unable to open icon.library !");
					}
					else err.NotifyError("unable to open workbench.library !");
				}
				else err.NotifyError("unable to open utility.library !");
			}
			else err.NotifyError("unable to open gadtools.library !");
		}
		else err.NotifyError("unable to open graphics.library !");
	}
	else err.NotifyError("unable to open intuition.library !");

	if(!ok) return FALSE;


	/* normal startup-code.... */

	ok=FALSE;

	if(alen)
	{
		/* start from CLI */
		/* parse arguements */

		if(rda = (RDArgs *)AllocDosObject( DOS_RDARGS, NULL ))
		{
			rda->RDA_ExtHelp = (UBYTE *)"This could be an extended help - here you get nothing :-)\n";

			if(ReadArgs( (UBYTE *)TEMPLATE, (LONG *) &args, rda ) )
			{
				/* Args erfolgreich gelesen */

				/* und ausgeben */

				cout << "Readargs: "<< endl;

				cout << "STRING: "  << args.string     << endl;
				cout << "INTEGER: " << *(args.integer) << endl;
				cout << "TEST: "    << args.test       << endl;

				ok=TRUE;
			}
			else
			{
				err.DosError(IoErr(), "Error parsing commandline");	/* prints the appropriate err message */
			}
		}
		else
			err.NotifyError("Out of memory error !");
	}
	else
	{
		/* Workbench-Start */

		ToolTypes = (UBYTE **)ArgArrayInit((LONG)alen,(UBYTE **)aptr);

		if(ToolTypes)
		{
			/* open a windows for Input() und Output()                  */
			/* this is only sometimes useful. If you don't do something */
			/* similar, you won't be able to output something to stdout */
			/* (cout, cin)                                              */
			/* necessary because startupcode initializes stdin=Input()  */
			/* and stdout=Output(), which is both zero if called via WB */

			DisplayBeep(0);

			stdin=(BPTR)Open((STRPTR)ArgString(ToolTypes,(STRPTR)"WINDOW", (STRPTR) "CON:0/0/640/200/Startup Code demo window/AUTO/CLOSE/WAIT"),MODE_NEWFILE);
			stdout=stdin;
			stdin_WB=stdin;

			SelectInput(stdin);
			SelectOutput(stdout);

			cin.attach(Input());	// let cin and cout
			cout.attach(Output());	// notify our Input() and Output() changed


			/* look here at all your supported tooltypes ! */
			/* we take defaults if no arguements are given */
			/* NOTE: this is different from CLI !          */

			args.string    =(char *)ArgString(ToolTypes, (STRPTR)"STRING",  (STRPTR)args.string);

			*(args.integer)=        ArgInt(   ToolTypes, (STRPTR)"INTEGER", (LONG)  99L);

			args.test      =(char *)ArgString(ToolTypes, (STRPTR)"TEST",    (STRPTR)args.test);


			/* Just as an example, normally you would do some other things... */

			cout << "ArgArrayInit:" << endl;

			cout << "STRING: "  << args.string     << endl;
			cout << "INTEGER: " << *(args.integer) << endl;
			cout << "TEST: "    << args.test       << endl;

			ok=TRUE;
		}
	}

	cin.tie(Output());		// to flush output before input is done
							// do this before you get any input !
							// (but after we have stdout and stdin defined if called via wb)

	return ok;
}




	/*
	 * Here the testprogram starts, uses some misc features
	 *
	 */


int main(int alen, char *aptr)
{
	if(MyInit(alen,aptr))		// all things went well
	{
		char String[100];		// in C++ is is possible to declare variables everywhere, not only at the top of a function


		// demonstrate some in/outputs

		cout << "Please enter string: ";
		cin.get(String,100);			 // save variant

		cout << String << endl;

		cout << "Please enter secound string: " << flush;
		cin >> String;					// only 80 bytes are read in for security reasons

		cout << String << endl;



		// do the same with some integers

		LONG x_a=0, x_b=0, y_a=0, y_b=0;


		ShowRequest(NULL, (UBYTE *)"Demo window",
			(UBYTE *)"Note that you can write 0x... for hex numbers,\nand 0... for octal numers",
			(UBYTE *)"Yes, my lord");

		cin.clear();			// we want to have a clear the state of cin

		while( (!cin) || (x_a < 2) || (x_a > 36) )
		{
			cin.clear();
			cout << "Please enter base_from :";
			cin >> x_a;
			if(cin.good())
				cout << "= " << x_a << endl;
			else
				cin.ignore(9999,'\n');	// skip all characters till EOF or '\n'
		}

		while( (!cin) || (x_b < 2) || (x_b > 36) || (x_b < x_a) )
		{
			cin.clear();
			cout << "Please enter base_to :";
			cin >> x_b;
			if(cin.good())
				cout << "= " << x_b << endl;
			else
				cin.ignore(9999,'\n');
		}

		do
		{
			cin.clear();
			cout << "Please enter number_from :";
			cin >> y_a;
			if(cin.good())
				cout << "= " << y_a << endl;
			else
				cin.ignore(9999,'\n');
		}
	   	while(!cin);

		do
		{
			cin.clear();
			cout << "Please enter number_to :";
			cin >> y_b;
			if(cin.good())
				cout << "= " << y_b << endl;
			else
				cin.ignore(9999,'\n');

		}
		while( (!cin) || (y_b < y_a) );



		// allocate memory. I am sure you could do better. But remeber,
		// this is only an example

		xxxx xyz;


		// do some output

		if (xyz.getmem())
		{
			ULONG x;
			BOOL stop=FALSE;

			for(x=x_a;(x<=x_b) && (!stop);x++)
			{
				LONG y;
				for(y=y_a;(y<=y_b) && (!stop) ; y++)
				{
					cout << "Base:" << x << ' ' << ltostr(y,xyz.getmem(),1000,TRUE,x);
					cout << endl << flush;


					// if the user wants to abort the output, he can..

					if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C) /* Hit since last check? */
					{
					 	SetSignal(0L,SIGBREAKF_CTRL_C);	/* Clear old status */
						cout << "*** BREAK\n" << endl;
						stop=TRUE;
					}
				}
			}

			// Show Clipboard

			ULONG len;

			if(len=LoadClip((UBYTE *)xyz.getmem(),10000))
			{
				char *s=xyz.getmem();
				s[len]=0;
				ShowRequest(NULL, (UBYTE *)"Contents of the clipboard", (UBYTE *)xyz.getmem(),(UBYTE *)"OK, I saw the clipboard");
			}

		}
		else cout << "Out of memory error !" << endl;



		// demonstrate class editfile

		cout << "Please enter filename to read: ";
		cin.get(String,100);

		err.Clear();

		editfile ef(String);

		if(err)
		{
			cout << "Error reading file \"" << String << "\"";
			OutputError();
		}
		else
		{
			cout << String << " has " << ef.strings << " lines." << endl;

			cout << "The first three lines are:" << endl;

			for(ULONG x=0; (x<3) && (x < ef.strings); x++)
				cout << ef.index[x] << endl;
		}

		cout << endl;




		// demonstrate CED_Support


		// look for CED

		MsgPort *CED_Port=FindPort((STRPTR)"rexx_ced");

		if(CED_Port)
		{
			cout << "You have CED in memory" << endl;
			cout << "Is it Version 3.5 (or higer)? (y/n) ";

			// the functions in CED_support only work with CED 3.5+, so we have
			// to ask the user

			char ok=0;

			while( (ok != 'n') && (ok != 'y'))
			{
				cin >> ok;
			}

			if(ok=='n')
			{
				cout << "Upgrade as fast as possible" << endl;
			}
			else
			{
				// Send some strings to CED

				SendCygnusEdMessage("cedtofront");
				SendCygnusEdMessageGetReply("status restname"); /* Name der Datei im aktuellen View */
				char *s=GetStringFromArexx();			// allocates storage and copies the result into it

				char *Buffer;
				if(Buffer=(char *)AllocVec(1024,MEMF_ANY))
				{
					sprintf((UBYTE *)Buffer,(UBYTE *)"okay1 You are vieving file %s\n",s);
					SendCygnusEdMessage("cedtofront");
					SendCygnusEdMessage(Buffer);
					FreeVec((UBYTE *)Buffer);
				}
				else
					err.NotifyError("Out of memory error !");

				FreeVec(s);					// free allocated memory by GetStringFromArexx()
			}
		}
		else
		{
			cout << "You don't have CED in memory" << endl;
		}


		// try to allocate some memory

	    char *a = new char [10000];

		char *b = new char [500000];

		if(b)
			cout << "You had some memory free...\n";

		char *c = new char [1000000];
		if(c)
			cout << "Wow, you had even some more memory free...\n";


		char *d= new(MEMF_CLEAR) char [10000000];
		if(d)
			cout << "Wow, you must have really a lot of ram...\n";

		delete a;
		delete b;
		delete c;
		delete d;

	}	// MyInit()



	// if an error occurred...

	OutputError();

	cout << "end of demo program." << endl;


	/* the Dos-Printf */

	Printf((UBYTE *)"Hello, world ! %ld %ld %ld %ld\n",1,2,3,4);

	return(0);				// exit is called if we fall through
}




	/*
	 * Output error-message if we have one
	 *
	 */


void OutputError(void)
{
	if(err)
	{
		char *err_mem=0;
		char *es;
		LONG err_nr=err.SetErrorNumber(0);

		if(es=err.have_error_string())
		{
			cout <<	es << endl;
		}
		else
		{
			if(err_nr)
			{
				cout <<	"Error-Number: " << err_nr << endl;
			}
		}
	}

	err.Clear();
}
