/**************************************************************************
*  NAME: Joy2Key                                               _          *
*  FUNCTION: Translate joystick movement into key press     _ //   _      *
*  PROGRAMMER: Piero Filippin (omega@sabrina.unipd.dei.it)  \X/ _ //   _  *
*                                                               \X/ _ //  *
*  This program is Public Domain (donation pleased)                 \X/   *
*                                                                         *
**************************************************************************/

#define IFFIRE 0x4C,0        /*cursor up         */
#define IFLEFT 0x4F,0        /*cursor left       */
#define IFRIGHT 0x4E,0       /*cursor right      */
#define IFDOWN 0x4D,0        /*cursor down       */
#define IFUP 0x4C,0          /*cursor up         */
#define CODETOQUIT 0x10      /* q +              */
#define QUALIFIERTOQUIT 0x19|IEQUALIFIER_RELATIVEMOUSE /* LALT+LSHIFT+CTRL */
#define TIMEDELAY 5

#include <exec/types.h>
#include <clib/alib_protos.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <string.h>
#include <exec/interrupts.h>
#include <devices/input.h>
#include <hardware/custom.h>
#include <hardware/cia.h>

#define CIAAPRA 0xBFE001
#define FIRE   1
#define RIGHT  2
#define LEFT   4
#define DOWN   8
#define UP    16
#define PORT1 1
#define PORT2 2

/* Funzioni */
void main(void);
void RemoveHandler(void);
void InstallHandler(void);
void RawInsert(short code,short qualifier); /* insert RAWKEY event */
UBYTE Joystick(UBYTE  port ); /* returns joy status */

struct handlerdata {
	struct Task *thistask;
	short sigbit;
	long code;
	long qualifier;
};

struct MsgPort *inputDevPort;
struct Interrupt handlerStuff;
struct IOStdReq *inputRequestBlock;
struct handlerdata data;
struct Custom *custom =(struct Custom *)0xDFF000; /* this in no sure, but don't need amiga lib */
struct CIA *cia = (struct CIA *) CIAAPRA;

/* Declarations for CBACK */
extern BPTR _Backstdout;         /* standard output when run in background */
long _BackGroundIO = TRUE;       /* Flag to tell it we want to do I/O      */
long _stack = 1024;              /* Amount of stack space our task needs   */
char *_procname = "Joy2Key";     /* The name of the task to create         */
long _priority = 0;              /* The priority to run us at              */

void Print(char *stringa); /* We are running in background, so cannot use printf()*/

void Print(char *stringa)
{
	Write(_Backstdout,stringa,strlen (stringa));
}

void main(void)
{
  UBYTE value = 0;
	Print("\n\nJoy2Key v1.0 - By Piero Filippin\n\n");
#ifndef DEBUG
	Close(_Backstdout);
#endif
	data.code=CODETOQUIT;
	data.qualifier=QUALIFIERTOQUIT;
	data.sigbit = AllocSignal(-1);
	data.thistask=FindTask(NULL);
	InstallHandler();
	while (!((data.thistask->tc_SigRecvd) & (1 << data.sigbit))) { /* we cannot use Wait() */
    value = Joystick( PORT2 );
    if( value & FIRE ) {
#ifdef DEBUG
			Print("FIRE\n");
#endif
      RawInsert(IFFIRE);
    }
    if( value & RIGHT ) {
#ifdef DEBUG
			Print("RIGHT\n");
#endif
      RawInsert(IFRIGHT);
    }
    if( value & LEFT ) {
#ifdef DEBUG
			Print("LEFT\n");
#endif
      RawInsert(IFLEFT);
    }
    if( value & DOWN ) {
#ifdef DEBUG
			Print("DOWN\n");
#endif
      RawInsert(IFDOWN);
    }
    if( value & UP ) {
#ifdef DEBUG
			Print("UP\n");
#endif
      RawInsert(IFUP);
    }
		Delay(TIMEDELAY);
  }
	RemoveHandler();
#ifdef DEBUG
	Close(_Backstdout);
#endif
	FreeSignal(data.sigbit);
	Exit(0);
}

struct InputEvent *myhandler(struct InputEvent *event,struct handlerdata *data)
{
	register struct InputEvent *ep, *lastevent;
	for (ep = event, lastevent = NULL; ep != NULL; ep = ep->ie_NextEvent) {
		if ((ep->ie_Class == IECLASS_RAWKEY) && (ep->ie_Code  == data->code) && (ep->ie_Qualifier == data->qualifier)) {
			if (lastevent == NULL) event = ep->ie_NextEvent;
			else lastevent->ie_NextEvent = ep->ie_NextEvent;
			lastevent = ep;
			Signal(data->thistask,1 << data->sigbit);
		}
	}
	return(event);
}

void RemoveHandler(void)
{
	if (inputRequestBlock) {
		if (inputRequestBlock->io_Device) {
			inputRequestBlock->io_Command = IND_REMHANDLER;  /* Remove handler */
			inputRequestBlock->io_Data    = (APTR)&handlerStuff;
			DoIO((struct IORequest *)inputRequestBlock);
			CloseDevice((struct IORequest *)inputRequestBlock);
		}
		DeleteStdIO(inputRequestBlock);
	}
	if (inputDevPort) {
		DeletePort(inputDevPort);
	}
	return;
}

void InstallHandler(void)
{
	inputRequestBlock = NULL;
	if (!(inputDevPort = CreatePort(0L, 0L))) {
		RemoveHandler();
	}
	if (!(inputRequestBlock = CreateStdIO(inputDevPort))) {
		RemoveHandler();
	}
	if (OpenDevice("input.device", 0L,(struct IORequest *)inputRequestBlock, 0L)) {
		RemoveHandler();
	}
	handlerStuff.is_Data = (APTR)&data;         /* Set up for installation of */
	handlerStuff.is_Code = myhandler;          /* myhandler.                 */
	handlerStuff.is_Node.ln_Pri = 51;         /* Ahead of intuition, please */
	inputRequestBlock->io_Command = IND_ADDHANDLER;
	inputRequestBlock->io_Data    = (APTR)&handlerStuff;
	DoIO((struct IORequest *)inputRequestBlock);   /* Add me. */
	return;
}

void RawInsert(short code,short qualifier) {
	/* Set up an input request */
	struct InputEvent MyNewEvent;
	inputRequestBlock->io_Command = IND_WRITEEVENT;
	inputRequestBlock->io_Flags   = 0L;
	inputRequestBlock->io_Length  = (long)sizeof(struct InputEvent);
	inputRequestBlock->io_Data    = (APTR)&MyNewEvent;
	MyNewEvent.ie_Class = IECLASS_RAWKEY;
	MyNewEvent.ie_Code = code;
	MyNewEvent.ie_Qualifier = qualifier;
	DoIO((struct IORequest *)inputRequestBlock);
}

UBYTE Joystick(UBYTE  port )
{
	UBYTE data = 0;
	UWORD joy;
	if( port == PORT1 ) {
		joy = custom->joy0dat;
		data += !( cia->ciapra & 0x0040 ) ? FIRE : 0;
	}
	else {
		joy = custom->joy1dat;
		data += !( cia->ciapra & 0x0080 ) ? FIRE : 0;
	}
	data += joy & 0x0002 ? RIGHT : 0;
	data += joy & 0x0200 ? LEFT : 0;
	data += (joy >> 1 ^ joy) & 0x0001 ? DOWN : 0;
	data += (joy >> 1 ^ joy) & 0x0100 ? UP : 0;
	return( data );
}
