/**************************************************************************
*  NAME: Macro                                                 _          *
*  FUNCTION: Record and reinsert a key sequence             _ //   _      *
*  WRITTEN BY: Piero Filippin (omega@sabrina.unipd.dei.it)  \X/ _ //   _  *
*                                                               \X/ _ //  *
*  This program is Public Domain (donation pleased)                 \X/   *
*                                                                         *
**************************************************************************/

#ifndef BUFFER
#define BUFFER 4000
#endif

#define IDLE  FALSE
#define RECORDING TRUE
#define QUALIFIER 0x19 /* LALT LSHIFT CTRL */
#define START 0x1A /* [ */
#define STOP 0x1B /* ] */
#define INSERT 0x17 /* i */
#define CLEAR 0x12 /* e */
#define QUIT 0x10 /* q */
#define INTUINAME "intuition.library"
#define QUITSG (1<<data.quit)
#define STARTSG (1<<data.startrec)
#define STOPSG (1<<data.stoprec)
#define INSERTSG (1<<data.insert)
#define CLEARSG (1<<data.clear)
#define STORESG (1<<data.mymsgport->mp_SigBit)

#define TITLE           "MACRO v1.1 - By Piero Filippin"
#define IDLEMSG         "MACRO Status: Idle"
#define RECORDINGMSG    "MACRO Status: Recording..."
#define CLEARMSG        "MACRO Status: Buffer cleared"
#define ENDRECORDINGMSG "MACRO Status: End recording"
#define QUITMSG         "MACRO Status: Quitting..."
#define INSERTMSG       "MACRO Status: Codes inserted"
#define FULLMSG         "WARNING: Buffer FULL"
#define NOMEMMSG        "Could NOT allocate memory!"

/* Declarations for CBACK */
extern BPTR _Backstdout;         /* standard output when run in background */
#ifdef DEBUG
long _BackGroundIO = 1;          /* Flag to tell it we want to do I/O      */
char string[11];
#else
long _BackGroundIO = 0;          /* Flag to tell it we don't want to do I/O*/
#endif
long _stack = 4000;              /* Amount of stack space our task needs   */
char *_procname = "MACRO";       /* The name of the task to create         */
long _priority = 1;              /* The priority to run us at              */

/* Funzioni */
#ifdef DEBUG
extern void Print(char *string,BOOL newline); /* Print string to _Backstdout */
extern char *ltoh(char *string,long val);
#endif
extern void RawInsert(long code,long qualifier);
extern void RemoveHandler(void);
extern void InstallHandler(void);

void main(void);

struct handlerdata { 
	struct MsgPort *mymsgport;
	struct Task *thistask;
	short quit;
	short startrec;
	short stoprec;
	short insert;
	short clear;
	short store;
};

struct RawMsg {
	struct Message message;
	UWORD code;
	UWORD qualifier;
};
	
struct MsgPort *inputDevPort;
struct Interrupt handlerStuff;
struct IOStdReq *inputRequestBlock;
struct handlerdata data;

#ifdef INFOWINDOW
struct IntuitionBase *IntuitionBase;
struct Window *MyWindow;
struct NewWindow NewWindow = {
	100, /*LeftEdge   */
	50,  /*TopEdge    */
	300, /*Width      */
	11,  /*Height     */
	0,   /*DetailPen  */
	1,   /*BlockPen   */
	NULL,/*IDCMPFlags */
	SMART_REFRESH | WINDOWDRAG | WINDOWDEPTH,/*Flags      */
	NULL,/*FirstGadget*/
	NULL,/*CheckMark  */
	(UBYTE *)TITLE,/*Title      */
	NULL,/*Screen     */
	NULL,/*BitMap     */
	0,   /*MinWidth   */
	0,   /*MinHeight  */
	0,   /*MaxWidth   */
	0,   /*MaxHeight  */
	WBENCHSCREEN/*Type*/
};
#endif

void main(void) {
	short status = IDLE;
	long bitmask = NULL;
	char *buff;
	UWORD *pointer;
	struct RawMsg *myrawmsg;

	#ifdef INFOWINDOW
	if(!(IntuitionBase=(struct IntuitionBase *)OpenLibrary(INTUINAME,0))) {
		#ifdef DEBUG
		Print("Could NOT open the Intuition Library!",TRUE);
		#endif
		Exit(20);
	}
  if(!(MyWindow = (struct Window *) OpenWindow( &NewWindow ))) {
		CloseLibrary((struct Library*) IntuitionBase );
		#ifdef DEBUG
		Print("Could NOT open the Window!",TRUE);
		#endif
		Exit(30);  
	}
	#endif

	data.quit     = AllocSignal(-1);
	data.startrec = AllocSignal(-1);
	data.stoprec  = AllocSignal(-1);
	data.insert   = AllocSignal(-1);
	data.clear    = AllocSignal(-1);
	data.thistask=FindTask(NULL);	

	data.mymsgport=CreatePort(0,0);

	if (!(buff= AllocMem(BUFFER-(BUFFER % 4),MEMF_CLEAR))) {
		#ifdef DEBUG
		Print(NOMEMMSG,TRUE);
		#endif
		#ifdef INFOWINDOW
		SetWindowTitles(MyWindow,NOMEMMSG,(char *)-1);
		Delay(1*50);
		#endif
		goto quit;
	}

	#ifdef DEBUG
	else {
		Print("MACRO: allocated a ",FALSE);	
		Print(ltoh(string,(long)( BUFFER-(BUFFER % 4) ) ),FALSE);
		Print(" bytes long memory block at ",FALSE);
		Print(ltoh(string,(long)buff),TRUE);
	}
	#endif

	pointer=(UWORD *)buff;
	
	InstallHandler();

	while ( !(bitmask & (1<<data.quit)) ) {
		bitmask = Wait(QUITSG | STARTSG | STOPSG | INSERTSG | CLEARSG |STORESG );
		if (bitmask & STORESG) {
			while (myrawmsg=(struct RawMsg *)GetMsg(data.mymsgport)) {
				#ifdef DEBUG
				Print("Keycode: ",FALSE);
				Print(ltoh(string,(long)myrawmsg->code),FALSE);
				Print(" - Qualifier: ",FALSE);
				Print(ltoh(string,(long)myrawmsg->qualifier),TRUE);
				#endif
				if ((status == RECORDING) &&(myrawmsg->code != NULL)) {
					*(pointer++)=myrawmsg->code;
					*(pointer++)=myrawmsg->qualifier;
					#ifdef DEBUG
					Print("Keypress stored\nBuffer usage: ",FALSE);
					Print(ltoh(string,(long)((long)pointer - (long)buff)),FALSE);
					Print(" Buffer storage capacity: ",FALSE);
					Print(ltoh(string,(long)(BUFFER-(BUFFER % 4))),TRUE);
					#endif
					if (pointer>=(UWORD *)buff+(BUFFER-(BUFFER % 4))) {
						status=IDLE;
						#ifdef DEBUG
						Print(FULLMSG,TRUE);
						#endif
						#ifdef INFOWINDOW
						SetWindowTitles(MyWindow,FULLMSG,(char *)-1);
						Delay(1*50);
						#endif
					}
				}
				else{
					#ifdef INFOWINDOW
					SetWindowTitles(MyWindow,IDLEMSG,(char *)-1);
					#endif
				}
			FreeMem(myrawmsg,sizeof(struct RawMsg));
			}
		}

		if (bitmask & CLEARSG) {
			#ifdef DEBUG 
			Print(CLEARMSG,TRUE);
			#endif
			pointer=(UWORD *)buff;
			#ifdef INFOWINDOW
			SetWindowTitles(MyWindow,CLEARMSG,(char *)-1);
			#endif
		}

		if ((bitmask & STARTSG) && (status == IDLE)) {

			#ifdef DEBUG
			Print(RECORDINGMSG,TRUE);
			#endif

			status=RECORDING;

			#ifdef INFOWINDOW
			SetWindowTitles(MyWindow,RECORDINGMSG,(char *)-1);
			#endif

		}

		if ((bitmask & STOPSG) && (status == RECORDING)) {
			#ifdef DEBUG
			Print(ENDRECORDINGMSG,TRUE);
			#endif

			status=IDLE;

			#ifdef INFOWINDOW
			SetWindowTitles(MyWindow,ENDRECORDINGMSG,(char *)-1);
			#endif
		}

		if ((bitmask & INSERTSG) && (status == IDLE)) {
			UWORD *tempptr = (UWORD *)buff;
			#ifdef DEBUG
			Print(INSERTMSG,TRUE);
			#endif

			while (tempptr<pointer) {
				RawInsert(*(tempptr++),*(tempptr++));
			}

			#ifdef INFOWINDOW
			SetWindowTitles(MyWindow,INSERTMSG,(char *)-1);
			#endif
		}

	}

	#ifdef DEBUG
	Print(QUITMSG,TRUE);
	#endif

	#ifdef INFOWINDOW
	SetWindowTitles(MyWindow,QUITMSG,(char *)-1);
	Delay(2*50);
	#endif

	RemoveHandler();
quit:
	while (myrawmsg=(struct RawMsg *)GetMsg(data.mymsgport)) {
		FreeMem(myrawmsg,sizeof(struct RawMsg));
	}
	DeletePort(data.mymsgport);
	FreeMem(buff,(BUFFER-(BUFFER % 4)));

	#ifdef INFOWINDOW
  DisplayBeep( MyWindow->WScreen );
	CloseWindow( MyWindow );
	CloseLibrary((struct Library*) IntuitionBase );
	#endif

	#ifdef DEBUG
	Close(_Backstdout);
	#endif

	Exit(0);
}

struct InputEvent *myhandler(struct InputEvent *event,struct handlerdata *data) {
	register struct InputEvent *ep, *lastevent;
	register struct RawMsg *myrawmsg;
	for (ep = event, lastevent = NULL; ep != NULL; ep = ep->ie_NextEvent) {
		if (ep->ie_Class == IECLASS_RAWKEY) {
			if (!(ep->ie_Qualifier == (QUALIFIER | IEQUALIFIER_RELATIVEMOUSE))) {
				myrawmsg=(struct RawMsg *)AllocMem(sizeof(struct RawMsg),MEMF_CLEAR);
				myrawmsg->code = ep->ie_Code;
				myrawmsg->qualifier = ep->ie_Qualifier;
				myrawmsg->message.mn_ReplyPort = NULL;
				PutMsg(data->mymsgport,(struct Message *)myrawmsg);
			}
			else {
				switch (ep->ie_Code) {
					case QUIT:
						Signal(data->thistask,1<<data->quit);
						break;
					case START:
						Signal(data->thistask,1<<data->startrec);
						break;
					case STOP:
						Signal(data->thistask,1<<data->stoprec);
						break;
					case INSERT:
						Signal(data->thistask,1<<data->insert);
						break;
					case CLEAR:
						Signal(data->thistask,1<<data->clear);			
						break;
					default:
						goto noremove;
						break;
				} 
				if (lastevent == NULL) event = ep->ie_NextEvent; /* Remove */
				else lastevent->ie_NextEvent = ep->ie_NextEvent; /* handled events */
			}
noremove: ;
		}
		else lastevent = ep;  
	}
	return(event);
}
