#include <amiga.h>
#include "rexx/storage.h"
#include "rexx/errors.h"
#include "rexx/rxslib.h"
#include "avideo.h"
#if AVLIB
   #include "avlib.h"
#endif

long _stack = 4000;
long _priority = 0;
long _BackGroundIO = 1;
char *_procname = "AVideo";
struct GfxBase *GfxBase;
struct IntuitionBase *IntuitionBase;
struct Library *AVideoBase;
struct RxsLib  *RexxSysBase;    /* ARexx library base pointer  */

static struct MsgPort *rexxPort; /* our message port */
static char *portName = AVIDEOPORT; /* our rexx port name */
static struct BitMap loadBm; /* BitMap to load picture in */
static struct AVideoMsg rexxMsg ;
static char errMsg[256];
static struct View  **views;
static BPTR curDir;

static UWORD  UpRexx(char *portName);
static UWORD  DownRexx( void );
static WORD  Parse(char **argv, char  *inCmdLn, char  *outArgLn);
static WORD  HandleRexx( struct RexxMsg *msg );
static WORD  DispatchRexx(short argc, char **argv);
static WORD HandleCmdLn( struct AVideoMsg *msg );
static WORD  HandleMsg( struct AVideoMsg *msg );
static UWORD  AVCmds( struct AVideoMsg *msg );
static UWORD ErrMsg( char *errMsg, UWORD error );

#define LOADVIEWS -2

long main()
{
   WORD error = 0;
	LONG waitSigs,sigs;
	struct AVideoMsg *msg;

	if (FindPort(portName)) {
		printf("AVIDEO server dismounted !\n");
      Execute("avcmd avquit",0L,0L);
      Delay(50L);
		return(0);
	}
	GfxBase = OpenLibrary("graphics.library",0L);
	IntuitionBase = OpenLibrary("intuition.library",0L);
   #if AVLIB
    if ((AVideoBase = OpenLibrary("avideo.library",0L)) == NULL) {
       printf("'avideo.library' not found\n");
       return(100);
    } else {
		printf("(C) ARCHOS - AVideo %s - Ver %d\n",
			AVType()==AVIDEO12 ? "12" : "24", AVideoBase->lib_Version);
	 }
   #endif

	rexxMsg.bm = &loadBm ;

	if (AVOpen()) {
		printf("Error while opening AVideo server\n");
		error = 101;
		goto END;
	}
	if (UpRexx(portName)) {
		printf("Error while opening AVideo rexx port\n");
		error = 102;
		goto END;
	}
   CurrentDir((APTR)(curDir = (BPTR)CurrentDir(0)));
   waitSigs = (1L << rexxPort->mp_SigBit) | SIGBREAKF_CTRL_C;

	while (TRUE) {
		sigs = Wait( waitSigs );
	   if (sigs & (1L << rexxPort->mp_SigBit)) {
  		   while (msg = (struct RexxMsg *)GetMsg(rexxPort)) {
   			if (CheckRexxMsg(msg))
	   			error = HandleRexx(msg);
		   	else if (msg->argc)
	   			error = HandleCmdLn(msg);
            else
			   	error = HandleMsg(msg);
   			ReplyMsg(msg);
			}
		}
	   if  (error==-1  ||  (sigs & SIGBREAKF_CTRL_C))
			break;
   }
	if ( error != -1 ) {
		rexxMsg.cmd  = AVQUIT ;
		error = AVCmds( &rexxMsg );
		ErrMsg(errMsg, error);
   }
   error = 0 ;
END :
   if (AVideoBase)
   	CloseLibrary(AVideoBase);
	AVClose();
	DownRexx();
	CloseLibrary(GfxBase);
	CloseLibrary(IntuitionBase);
   CurrentDir((APTR)curDir);
	return(error);
}

/* HandleRexx -- handler REXX messages
 */
static WORD HandleRexx( struct RexxMsg *msg )
{
   STRPTR  saveArg0;
   LONG  saveAction;
	WORD  error;
   ULONG  lnBuf[256/4];

   msg->rm_Result1 = 0 ;
   msg->rm_Result2 = 0 ;
   saveArg0        = msg->rm_Args[0];
   saveAction      = msg->rm_Action;
  	msg->rm_Action  = Parse(&msg->rm_Args[0],msg->rm_Args[0],lnBuf);
	error           = DispatchRexx(msg->rm_Action,msg->rm_Args);
	msg->rm_Result1 = 0;
	if (error)
		msg->rm_Result1 = ErrMsg(errMsg, error); /* print error msg and return arexx error */
   msg->rm_Args[0] = saveArg0;
   msg->rm_Action  = saveAction;
	return(error);
}

/* HandleCmdLn -- handle DOS command lines
 */
static WORD HandleCmdLn( struct AVideoMsg *msg )
{
	WORD  error;

   if (msg->sup) /* msg->sup contains the current directory lock */
      CurrentDir((APTR)msg->sup);
	if ((error = DispatchRexx(msg->argc,&msg->argv[0]))  &&  error !=-1) {
		msg->error = ErrMsg(errMsg, error); /* print error msg and return arexx error */
      msg->errMsg = errMsg;
   }
	return(error);
}

/* HandleMsg -- handle messages that ere not sent by AREXX
 */
static WORD  HandleMsg( struct AVideoMsg *msg )
{                 
   WORD  error;

	error = AVCmds( msg );
   if (error && error !=-1 ) {
		msg->error = ErrMsg(errMsg, error); /* print error msg and return arexx error */
      msg->errMsg = errMsg;
   }
   return(error);
}

/* DispatchRexx -- server's command interpreter
 */
static WORD DispatchRexx( WORD  argc,  char **argv )
{
	WORD  error = 0;
	LONG  prefix = *(WORD *)&argv[0][0];
	LONG  suffix = *(LONG *)&argv[0][2];
	char  *name = NULL;
	WORD  mode = 0;

   if (prefix == '\0\0AV') {
	   switch (suffix) { /* commands are in the form AVXXX and aligned */
			case((LONG)'LOAD'): /* AVLOAD : load picture into memory */
				switch (argc) {
					case(2) : name = argv[1]; break;
					case(3) : name = argv[1]; mode = atoi(argv[2]);  break;
				}
            if (mode < 0)
               mode = 0;
				rexxMsg.cmd  = AVLOAD ;
				rexxMsg.mode = mode ;
				rexxMsg.name = name ;
				error = AVCmds( &rexxMsg );
				break;
			case((LONG)'DISP'): /* AVDISPLAY */
				switch (argc) {
					case(2) : mode = atoi(argv[1]); break;
				}
            if (mode < 0)
               mode = 0;
				rexxMsg.cmd  = AVDISPLAY ;
				rexxMsg.mode = mode ;
				rexxMsg.name = NULL ;
				error = AVCmds( &rexxMsg );
				break;
			case((LONG)'SHOW'): /* AVSHOW */
				switch (argc) {
					case(2) : name = argv[1]; break;
					case(3) : name = argv[1]; mode = atoi(argv[2]);  break;
				}
            if (mode < 0)
               mode = 0;
				rexxMsg.cmd  = AVSHOW ;
				rexxMsg.mode = mode ;
				rexxMsg.name = name ;
				error = AVCmds( &rexxMsg );
				break;
			case((LONG)'FBUF'): /* AVFBUF */
				rexxMsg.cmd  = AVFBUFFER ;
				rexxMsg.mode = (argc==2)>0 ? atoi(argv[1]) : -2;
				rexxMsg.name = NULL ;
				error = AVCmds( &rexxMsg );
				break;
			case((LONG)'TRAN'): /* AVTRANSPAR */
				rexxMsg.cmd  = AVTRANSPAR ;
				rexxMsg.mode = (argc==2)>0 ? atoi(argv[1]) : -2;
				rexxMsg.name = NULL ;
				error = AVCmds( &rexxMsg );
				break;
			case((LONG)'CLEA'): /* AVCLEAR */
				rexxMsg.cmd  = AVCLEAR ;
				rexxMsg.mode = (argc==2)>0 ? atoi(argv[1]) : 0 ;
				rexxMsg.name = NULL ;
				error = AVCmds( &rexxMsg );
				break;
			case((LONG)'RESE'): /* AVRESET */
				rexxMsg.cmd  = AVRESET;
				rexxMsg.mode = (argc==2)>0 ? atoi(argv[1]) : 0 ;
				rexxMsg.name = NULL ;
				error = AVCmds( &rexxMsg );
				break;
			case((LONG)'FADE'): /* AVFADE */
				rexxMsg.cmd  = AVFADE;
				rexxMsg.mode = (argc==2) ? atoi(argv[1]) : 0 ;
				rexxMsg.name = NULL ;
				error = AVCmds( &rexxMsg );
				break;
			case((LONG)'RECO'): /* AVRECORD */
				rexxMsg.cmd  = AVRECORD;
				rexxMsg.mode = 0 ;
				rexxMsg.name = NULL ;
				error = AVCmds( &rexxMsg );
				break;
			case((LONG)'QUIT'): /* AVQUIT */
				rexxMsg.cmd  = AVQUIT ;
				rexxMsg.mode = 0 ;
				rexxMsg.name = NULL ;
				error = AVCmds( &rexxMsg );
				break ;
         case((LONG)'SWIT'): /* AVSWITCH */
				rexxMsg.cmd = AVSWITCH;
				rexxMsg.mode = (argc==2) ? atoi(argv[1]) : -1; /* default is -1 for this command */
				rexxMsg.name = NULL;
				error = AVCmds( &rexxMsg );
				break ;
         case((LONG)'LACE'): /* AVLACE */
				rexxMsg.cmd = AVLACE;
				rexxMsg.mode = (argc==2) ? atoi(argv[1]) : 0; 
				rexxMsg.name = NULL;
				error = AVCmds( &rexxMsg );
				break ;
			default :
				error = 254;
				break;
		}
	}
	return(error);
}

/* AVCmds -- dispatch AVideo commands
 */
static UWORD  AVCmds( struct AVideoMsg *msg )
{
	struct BitMap *bm = msg->bm;
	WORD  cmd = msg->cmd;
	char  *name = msg->name;
	WORD  mode = msg->mode;
	static char *picName;
	UWORD  error = 0;

   switch (cmd) { /* commands are in the form AVXXX and aligned */
		case(AVLOAD): /* AVLOAD : load picture into memory */
			if (name) {
            if (msg->sup) /* msg->sup contains the current directory lock */
                  CurrentDir((APTR)msg->sup);
				picName = name;
				if (error = AVLoadPict(picName,bm,NULL))
					picName = NULL;
            msg->vpModes    = AVPicModes();
            msg->colorTable = AVPicColors();
         } else {
				error = 252; /* wrong number of arguments */
			}
			break;
		case(AVDISPLAY): /* AVDISPLAY : display loaded picture */
			if (picName == NULL) {
				error = 253;
         } else {
				if(( error = AVLoadFBuf(picName,bm,mode, msg->vpModes, msg->colorTable)) == 0 ) {
					error   = AVFreeBm(bm);
					picName = NULL;
            }
			}
			break;
		case(AVSHOW): /* AVSHOW : load ans display image */
			msg->cmd = AVLOAD ;
			if (( error = AVCmds( msg ))==0 ) {
				msg->cmd = AVDISPLAY ;
				error = AVCmds( msg );
         }
			break;
		case(AVFBUFFER): /* AVFBUFFER : show frame buffer with fade in and out */
         error = AVFrameBuf(mode);
			break;
		case(AVTRANSPAR): /* AVTRANSPAR : turn transparency ON/OFF */
         error = AVTranspar(mode);
			break;
		case(AVCLEAR): /* AVCLEAR : clear frame buffer memory */
         if (mode)
            AVFade(-mode);
         error = AVClear();
         if (mode)
            AVFade(mode);
			break;
		case(AVRESET): /* AVRESET : reset AMIGAON no transparency */
         if (mode)
            AVFade(-mode);
         error = AVReset();
         if (mode)
            AVFade(mode);
			break;
		case(AVFADE): /* AVFADE : fade in or out */
			error = AVFade(mode);
			break;
		case(AVRECORD): /* AVRECORD : record current view */
			error = AVRecord(mode);
			break;
		case(AVLOADVIEWS): /* AVLOADVIEWS : load views permanently */
         error = AVLoadViews(msg->views,msg->endView);
			break;
      case(AVSWITCH): /* AVSWITCH : select buffer to display */
         error= AVSwitch(mode);
         break;
      case(AVLACE): /* AVLACE : select buffer to display */
         error= AVLace(mode);
         break;
		case(AVQUIT): /* return -1 as quit code */
			AVFreeBm(bm);
			AVClear();
			error = -1 ;
			break ;
		default :
			error = 254;
			break;
	}
	msg->error = error ;
	return(error);
}

/* UpRexx -- set up our rexxPort
 */
static UWORD  UpRexx( char *portName )
{
/*
	if (FindPort("REXX") == NULL)
      return(1);
   if ((RexxSysBase=(struct RxsLib *)OpenLibrary("rexxsyslib.library",0L)) == NULL)
		return(3);
 */
   RexxSysBase = (struct RxsLib *)OpenLibrary("rexxsyslib.library",0L);
   if ((rexxPort=CreatePort(portName,0L))  == NULL)
		return(4);
	return(0);
}

/* DownRexx -- clean rexx stuff
 */
static UWORD DownRexx( void )
{
   if (RexxSysBase)
	   CloseLibrary(RexxSysBase);
   RexxSysBase = NULL;
   if (rexxPort)
		DeletePort(rexxPort);
	rexxPort = NULL;
	return(0);
}

/* Parse -- parse command line 'inCmdLn' to build up argument line 'outArgLn'
 * and argument array 'argv'; return argc.
 */
static WORD  Parse(char **argv, char  *inCmdLn, char  *outArgLn)
{
	register char *cp = inCmdLn;
	register char *ap = outArgLn;
	register WORD c;
   register WORD argc;

	for (argc=0;    ;argc++) {
		while ((c=*cp)==' ' || c=='\t')
			cp++;
		if (c < ' ')
			break;
		if (c == '"') {
			cp++;
         *argv++ = ap;
			while (c = *cp++) {
				*ap++ = c;
				if (c == '"') {
					if (*cp == '"')
						cp++;
					else {
						ap[-1] = 0;
						break;
					}
				}
			}
		} else {
         *argv++ = ap;
			while ((c=*cp++) && c != ' ' && c != '\t')
				*ap++ = c;
			*ap++ = 0;
		}
		if (c == 0)
			--cp;
	}
	*ap = 0;
   if ((LONG)ap & 0x1) /* force word-alignment */
		*ap++ = 0;
   *argv = NULL;
   return(argc);
}

static UWORD ErrMsg( char *errMsg, UWORD error )
{
   if (AVError(error,errMsg))
      printf("AVIDEO : %s\n",errMsg);
   return(0);
}

