
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <dos/dos.h>
#include <exec/memory.h>
#include <exec/ports.h>

#define NUMBER	1
#define LLETTER 2
#define ULETTER 3
#define OTHER	4


struct IntuitionBase *IntuitionBase = NULL;
struct DOSBase	     *DOSBase	    = NULL;

struct Window *window;
struct Screen *screen;

struct Com
{
    struct  Com *Next;
    struct  Com *Prev;
    LONG    Command;
    LONG    Arg;
    LONG    Arg2;
} *Start = NULL, *End = NULL, *Tmp = NULL;

char ver[]="\0$VER: WSZ 1.3 (92.09.18)";


/*
** Closes down, and exits.
*/
void cldo(int err)
{
    if(DOSBase)         CloseLibrary(DOSBase);
    if(IntuitionBase)   CloseLibrary(IntuitionBase);
    exit(err);
}


/*
** Opens up all needed libraries and stuff.
*/
void opup()
{
    IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 36);
    if(!IntuitionBase) cldo(30);

    DOSBase = (struct DOSBase *)OpenLibrary("dos.library", 36);
    if(!DOSBase) cldo(29);
}


/*
** Checks the type of character.
*/
LONG type(char tkn)
{
    if(tkn >= '0' && tkn <= '9') return(NUMBER);
    if(tkn >= 'a' && tkn <= 'z') return(LLETTER);
    if(tkn >= 'A' && tkn <= 'Z') return(ULETTER);
    if(tkn == '-' || tkn == '+' || tkn == '*' || tkn == '/') return(OTHER);
}


/*
** Convert a string into an integer.
*/
int conv(char *str)
{
    int pek=0, ret=0;

    for(;str[pek] != NULL; pek++)
    {
	ret *= 10;
	ret += (int)str[pek] - (int)'0';
	if(type(str[pek]) != NUMBER) return(-1);
    }
    return(ret);
}


/*
** Returns the value from a flag, for example: X50 returns 50
*/
int convarg(char *str)
{
    int tmp=1;

    if(str[1] == NULL) return(-1);
    if(type(str[1]) != NUMBER) tmp++;
    return(conv(&(str[tmp])));
}


/*
** Get active window and screen
*/
void getactive()
{
    ULONG lock;

    lock=LockIBase(0);
    window = IntuitionBase->ActiveWindow;
    screen = IntuitionBase->ActiveScreen;
    UnlockIBase(lock);
}


/*
** Default values
*/
void defaultvalues(SHORT *s)
{
    s[0] = window->LeftEdge;
    s[1] = window->TopEdge;
    s[2] = window->Width;
    s[3] = window->Height;
    s[4] = 0;
    s[5] = 0;
}


/*
** Screen values
*/
void screenvalues(SHORT *s, UBYTE what)
{
    if(what == 0 || what == 5) s[0] = 0;
    if(what == 1 || what == 5) s[1] = 0;
    if(what == 2 || what == 5) s[2] = screen->Width;
    if(what == 3 || what == 5) s[3] = screen->Height;
}


/*
** Prints the window status.
*/
void windowstatus()
{
    PutStr("\n Window.\n");
    VPrintf(" X-Pos  : %d\n", &(window->LeftEdge));
    VPrintf(" Y-Pos  : %d\n", &(window->TopEdge));
    VPrintf(" Width  : %d\n", &(window->Width));
    VPrintf(" Height : %d\n", &(window->Height));
    VPrintf(" Title  : %s\n", &(window->Title));
}


/*
** Prints the screen status.
*/
void screenstatus()
{
    LONG    depth;

    depth = (LONG)screen->BitMap.Depth;

    PutStr("\n Screen.\n");
    VPrintf(" X-Pos  : %d\n", &(screen->LeftEdge));
    VPrintf(" Y-Pos  : %d\n", &(screen->TopEdge));
    VPrintf(" Width  : %d\n", &(screen->Width));
    VPrintf(" Height : %d\n", &(screen->Height));
    VPrintf(" Depth  : %ld\n", &(depth));
    VPrintf(" Title  : %s\n", &(screen->Title));
}


/*
** Prints a list of all screens and windows.
*/
void viewscreens()
{
    struct Screen *tmpscreen;
    struct Window *tmpwindow;
    ULONG  lock;

    lock = LockIBase(0);
    tmpscreen = IntuitionBase->FirstScreen;
    UnlockIBase(lock);

    while(tmpscreen != NULL)
    {
	if(tmpscreen == screen) PutStr("*");
	else PutStr(" ");
	VPrintf(" Screen '%s'", &(tmpscreen->Title));
	VPrintf(" at address %ld\n", &(tmpscreen));
	tmpwindow = tmpscreen->FirstWindow;
	while(tmpwindow != NULL)
	{
	    if(tmpwindow == window) PutStr("*");
	    else PutStr(" ");
	    VPrintf("    Window '%s'", &(tmpwindow->Title));
	    VPrintf(" at address %ld\n", &(tmpwindow));
	    tmpwindow = tmpwindow->NextWindow;
	}
	tmpscreen = tmpscreen->NextScreen;
    }
}



/*
** Alloc memory for command, and link into list
*/
struct Com *alloccom()
{
    Tmp = (struct Com *)AllocVec(sizeof(struct Com), MEMF_PUBLIC | MEMF_CLEAR);
    if(Tmp)
    {
	if(Start == NULL && End == NULL)
	{
	    Start = End = Tmp;
	    Tmp->Next = NULL;
	    Tmp->Prev = NULL;
	}
	else
	{
	    End->Next = Tmp;
	    Tmp->Prev = End;
	    Tmp->Next = NULL;
	    End=Tmp;
	}
	return(Tmp);
    }
    return(FALSE);
}

/*
** Frees memory used by command list
*/
void freecom()
{
    Tmp = Start;

    while(Tmp != NULL)
    {
	Start = Tmp;
	Tmp = Tmp->Next;
	FreeVec(Start);
    }
}


/*
** Parses the commands
*/
int parsecommands(int ac, char **av)
{
    int x=1, x1=0;
    int t=0;

    while(x<ac)
    {
	switch(av[x][0])
	{
	    case 'l':
	    case 'L':
		loadscript(&(av[x][1]));
		break;
	    default:
		t=1;
		Tmp = alloccom();
		if(!Tmp) return(0);
		Tmp->Command = (LONG)av[x][0];
		if(type(Tmp->Command) != LLETTER && type(Tmp->Command) != ULETTER)
		{
		    switch(x1)
		    {
			case 0: Tmp->Command = 'X'; break;
			case 1: Tmp->Command = 'Y'; break;
			case 2: Tmp->Command = 'W'; break;
			case 3: Tmp->Command = 'H'; break;
		    }
		    t=0;
		    x1++;
		}
		if(type(av[x][t]) != NUMBER)
		{
		    Tmp->Arg2 = av[x][t];
		}
		Tmp->Arg = (LONG)convarg(&(av[x][t-1]));
		if(Tmp->Command == 'r' || Tmp->Command == 'R') Tmp->Arg2 = Tmp->Arg;
		break;
	}
	x++;
    }
    return(0);
}

/*
** Loads a script.
*/
int loadscript(char *name)
{
    BPTR    lock;
    char    file[80] = "S:";
    int     tmp;
    BPTR    fil;
    char    buffer[120];
    char    temp;

    lock = Lock(name, ACCESS_READ);
    if(!lock)
    {
	for(tmp=0; name[tmp] != NULL; file[tmp+2] = name[tmp], tmp++);
	file[tmp+2] = NULL;
	lock = Lock(file, ACCESS_READ);
	if(!lock) return(0);
	name = file;
    }

    /* Got a lock */

    fil = OpenFromLock(lock);

    temp=' ';

    while(temp == ' ')
    {
	temp = FGetC(fil);
	if(temp == 0x09 || temp == 0x0a) temp = ' ';
    }

    while(temp != -1)
    {
	if(temp == -1) break;
	Tmp = alloccom();
	Tmp->Command = temp;
	tmp=1;
	temp = NULL;
	while(temp != -1 && temp != ' ' &&  temp != 0x09 && temp != 0x0a)
	{
	    buffer[tmp++] = temp = FGetC(fil);
	}
	buffer[tmp-1] = NULL;
	if(type(buffer[1]) != NUMBER) Tmp->Arg2 = buffer[1];
	Tmp->Arg = convarg(buffer);
	if(temp == -1) break;
	UnGetC(fil, temp);
	temp=' ';
	while(temp == ' ')
	{
	    temp = FGetC(fil);
	    if(temp == 0x09 || temp == 0x0a) temp = ' ';
	}
	if(Tmp->Command == 'l' || Tmp->Command == 'L')
	{
	    Tmp->Prev->Next = NULL;
	    End=End->Prev;
	    FreeVec(Tmp);
	    loadscript(&(buffer[1]));
	}
    }
    Close(fil);
    UnLock(lock);
    return(0);
}


/*
** Frees all but num bitmaps in a window.
*/
void freebitmap(int num)
{
    BYTE depth;
    WORD bpr, row;
    LONG *pek;
    int  i;

    depth = screen->BitMap.Depth;
    bpr   = screen->BitMap.BytesPerRow;
    row   = screen->BitMap.Rows;

    if(num < 1) num = 1;
    if(num > 8) num = 8;

    Forbid();
    while(depth > num)
    {
	pek = (LONG *)screen->BitMap.Planes[depth-1];
	for(i=0; i < (bpr*row)/4; i++)
	{
	    *pek = 0x00000000;
	    pek++;
	}
	FreeMem(screen->BitMap.Planes[depth-1], bpr*row);
	depth--;
    }
    screen->BitMap.Depth = num;
    Permit();
}


/*
** Adds bitmaps
*/
BOOL addbitmap(int num)
{
    BYTE depth;
    WORD bpr, row;
    PLANEPTR pek;

    depth = screen->BitMap.Depth;
    bpr   = screen->BitMap.BytesPerRow;
    row   = screen->BitMap.Rows;

    Forbid();

    if(num < 1) num = depth+1;
    if(num > 8) num = 8;

    while(depth < num)
    {
	pek = (PLANEPTR)AllocMem(bpr*row, MEMF_CHIP | MEMF_CLEAR);
	if(!pek)
	{
	    screen->BitMap.Depth = depth;
	    Permit();
	    return(FALSE);
	}
	screen->BitMap.Planes[depth] = pek;
	depth++;
    }
    screen->BitMap.Depth = depth;
    Permit();
    return(TRUE);
}


/*
** Sends an IDCMP_CLOSEWINDOW message to the window.
*/
BOOL closewindow()
{
    struct IntuiMessage *imess;
    struct MsgPort *replyport;

    if(!(window->IDCMPFlags & IDCMP_CLOSEWINDOW)) return(FALSE);

    imess = (struct IntuiMessage *)AllocVec(sizeof(struct IntuiMessage), MEMF_PUBLIC | MEMF_CLEAR);
    if(!imess) return(FALSE);

    replyport = (struct MsgPort *)CreateMsgPort();
    if(!replyport) return(FALSE);

    imess->ExecMessage.mn_Node.ln_Type	= NT_MESSAGE;
    imess->ExecMessage.mn_ReplyPort	= replyport;
    imess->ExecMessage.mn_Length	= sizeof(struct IntuiMessage);
    imess->Class			= IDCMP_CLOSEWINDOW;
    imess->IDCMPWindow			= window;

    Forbid();
    PutMsg(window->UserPort, imess);
    Permit();

    WaitPort(replyport);

    DeleteMsgPort(replyport);
    FreeVec(imess);

    return(TRUE);
}


void updateposition(int x, SHORT *s)
{

    if(x != 2)
    {
	if((s[0] + s[2]) > screen->Width) s[2] = screen->Width - s[0];
	if((s[1] + s[3]) > screen->Height) s[3] = screen->Height - s[1];
	ChangeWindowBox(window, s[0], s[1], s[2], s[3]);
    }
    if(x != 1) MoveScreen(screen, s[4], s[5]);
}


/*
** Private command.  This function will NEVER be a part of a release.
** This function is for authors use ONLY. Compile and use this function
** at your own risk.
*/
#ifndef NOPRIV
BOOL privatecommand(LONG arg, LONG arg2)
{
    struct Screen *s, *d;
    char *t;
    int de;

    s = screen;
    d = (struct Screen *)arg;

    de = s->BitMap.Depth;

    while(de)
    {
	t = s->BitMap.Planes[de-1];
	s->BitMap.Planes[de-1] = d->BitMap.Planes[de-1];
	d->BitMap.Planes[de-1] = t;
	de--;
    }

    return(TRUE);
}
#endif


/*
** Main, guess why...
*/
main(int ac, char **av)
{
    SHORT  s[6];
    BOOL   upd=FALSE, mvscr = FALSE;
    LONG   tal=0;
    struct Window *tmpwin;
    struct Com dummy;

    opup();

    getactive();

    if(ac != 1)
    {
	defaultvalues(s);
    }
    else
    {
	screenvalues(s, 5);
	upd=TRUE;
    }

    parsecommands(ac, av);

    Tmp=Start;

    dummy.Next = Start;

    while(Tmp)
    {
#ifdef PRCOM
	printf("\n Command : %c\n", (char)Tmp->Command);
	printf(" Arg     : %d\n", Tmp->Arg);
	printf(" Arg2    : %d\n", Tmp->Arg2);
	printf(" Screen  : %d\n", screen);
	printf(" Window  : %d\n\n", window);
#endif
	switch(Tmp->Command)
	{
#ifndef NOPRIV
	    case 'p':
		privatecommand(Tmp->Arg, Tmp->Arg2);
		break;
#endif
	    case 'u':                       /* COMMAND:     Update      */
	    case 'U':                       /* SYNTAX:      U[w/s]      */
		switch(Tmp->Arg2)
		{
		    case 'W':
		    case 'w':
			updateposition(1, s);
			break;
		    case 's':
		    case 'S':
			updateposition(2, s);
			break;
		    default:
			updateposition(0, s);
			break;
		}
		break;

	    case 'E':                       /* COMMAND:     Left        */
	    case 'e':                       /* SYNTAX:      L[+|-]n     */
		if(Tmp->Arg == -1) break;
		switch(Tmp->Arg2)
		{
		    case '-':
			s[4] = s[4] - Tmp->Arg;
			break;
		    case '+':
			s[4] = s[4] + Tmp->Arg;
			break;
		    default:
			s[4] = Tmp->Arg - screen->LeftEdge;
			break;
		}
		mvscr = TRUE;
		break;

	    case 'T':                       /* COMMAND:     Top         */
	    case 't':                       /* SYNTAX:      T[+|-]n     */
		if(Tmp->Arg == -1) break;
		switch(Tmp->Arg2)
		{
		    case '-':
			s[5] -= Tmp->Arg;
			break;
		    case '+':
			s[5] += Tmp->Arg;
			break;
		    default:
			s[5] = Tmp->Arg - screen->TopEdge;
			break;
		}
		mvscr = TRUE;
		break;

	    case 'k':                       /* COMMAND:     Kill        */
	    case 'K':                       /* SYNTAX:      Kn          */
		if(Tmp->Arg != -1)
		{
		    tmpwin = window;
		    window = (struct Window *)Tmp->Arg;
		    closewindow();
		    window = tmpwin;
		}
		else closewindow();
		break;
	    case 'm':                       /* COMMAND:     bitMap      */
	    case 'M':                       /* SYNTAX:      M[+|-]      */
		if(Tmp->Arg2 == '-')
		{
		    freebitmap(Tmp->Arg);
		}
		else if(Tmp->Arg2 == '+')
		{
		    addbitmap(Tmp->Arg);
		}
		break;

	    case 'c':                       /* COMMAND:     Change      */
	    case 'C':                       /* SYNTAX:      C[W|S|R]n   */
		switch(Tmp->Arg2)
		{
		    case 'r':
		    case 'R':
			getactive();
			break;
		    case 's':
		    case 'S':
			if(Tmp->Arg == -1) break;
			screen = (struct Screen *)Tmp->Arg;
			break;
		    case 'w':
		    case 'W':
			if(Tmp->Arg == -1) break;
			window = (struct Window *)Tmp->Arg;
			break;
		}
		break;

	    case 'a':                       /* COMMAND:     Activate    */
	    case 'A':                       /* SYNTAX:      An          */
		if(Tmp->Arg < 1) break;
		tmpwin=window;
		while(tmpwin == window)
		{
		    Delay(Tmp->Arg);
		    getactive();
		}
		defaultvalues(s);
		break;

	    case 'r':                       /* COMMAND:     Repeat      */
	    case 'R':                       /* SYNTAX:      Rn          */
		if(Tmp->Arg < 0) break;
		if(Tmp->Arg == 0 || Tmp->Arg2 == 0) Tmp = &dummy;
		else
		{
		    Tmp->Arg--;
		    if(Tmp->Arg != 0) Tmp = &dummy;
		    else Tmp->Arg = Tmp->Arg2;
		}
		break;

	    case 'd':                       /* COMMAND:     Delay       */
	    case 'D':                       /* SYNTAX:      Dn          */
		if(Tmp->Arg > 0) Delay(Tmp->Arg);
		getactive();
		defaultvalues(s);
		break;

	    case 'v':
	    case 'V':
		viewscreens();
		break;

	    case 'f':                       /* COMMAND:     Front       */
	    case 'F':                       /* SYNTAX:      F           */
		if(Tmp->Arg != -1)
		{
		    WindowToFront((struct Window *)Tmp->Arg);
		}
		else WindowToFront(window);
		break;


	    case 'b':                       /* COMMAND:     Back        */
	    case 'B':                       /* SYNTAX:      B           */
		if(Tmp->Arg != -1)
		{
		    WindowToBack((struct Window *)Tmp->Arg);
		}
		else WindowToBack(window);
		break;

	    case 'z':                       /* COMMAND:     Zip         */
	    case 'Z':                       /* SYNTAX:      Z           */
		if(Tmp->Arg != -1)
		{
		    ZipWindow((struct Window *)Tmp->Arg);
		}
		else ZipWindow(window);
		break;

	    case 'i':                       /* COMMAND:     Info        */
	    case 'I':                       /* SYNTAX:      I[S|W]      */
		switch(Tmp->Arg2)
		{
		    case 's':
		    case 'S':
			screenstatus();
			break;
		    case 'w':
		    case 'W':
			windowstatus();
			break;
		    default:
			screenstatus();
			windowstatus();
			break;
		}
		break;

	    case 'h':                       /* COMMAND:     Height      */
	    case 'H':                       /* SYNTAX:  H[+|-|/|*|S]n   */
		tal++;

	    case 'w':                       /* COMMAND:     Width       */
	    case 'W':                       /* SYNTAX:  W[+|-|/|*|S]n   */
		tal++;

	    case 'y':                       /* COMMAND:     Y position  */
	    case 'Y':                       /* SYNTAX:  Y[+|-|/|*|S]n   */
		tal++;

	    case 'x':                       /* COMMAND:     X position  */
	    case 'X':                       /* SYNTAX:   X[+|-|/|*|S]n  */

		if(Tmp->Arg2 == 's' || Tmp->Arg2 == 'S') screenvalues(s, tal);
		else if(Tmp->Arg2 == '-') s[tal] -= Tmp->Arg;
		else if(Tmp->Arg2 == '+') s[tal] += Tmp->Arg;
		else if(Tmp->Arg2 == '/') s[tal] /= Tmp->Arg;
		else if(Tmp->Arg2 == '*') s[tal] *= Tmp->Arg;
		else s[tal] = Tmp->Arg;
		upd=1;
		break;
	}
	tal=0;
	Tmp = Tmp->Next;
    }

    if(upd) updateposition(1, s);
    if(mvscr) updateposition(2, s);

    freecom();

    cldo(0);
}
