/**********************************************/
/* Brandon's                                  */
/*  _______   __   __   ______                */
/* /\  ____\ /\ \ /\ \ /\__  _\               */
/* \ \ \___/ \ \ \\ \ \\/_/\ \/               */
/*  \ \ \  ___\ \ \\ \ \  \ \ \               */
/*   \ \ \/\_ \\ \ \\ \ \  \ \ \              */
/*    \ \ \/_\ \\ \ \\_\ \  \_\ \__           */
/*     \ \______\\ \______\ /\_____\          */
/*      \/______/ \/______/ \/_____/          */
/*       Graphical User      Interface        */
/*  - For the Comtech Operating System        */
/**********************************************/
/* Yes, it's true... we use a total of 2 C Library functions... */
#include <dos.h>                /* For int86(); */
#include <conio.h>              /* For getch(); */
#include <malloc.h>
#include <string.h>				/* For memset(); memcpy(); NULL */

/* Optimized Z-order by Sebastian Redl */
/* The windows in the array should be ordered by their z-order.
 * 0 is the topmost window. In order to make create new windows easier
 * this window will always be placed in the last array position.
 * When bringing a window to top a re-ordering is necessary, but this is faster
 * than doing a double-loop for every drawing. */

/* UPDATE */
/* At least I thought that's how it should be done. But since your system
 * also uses the array position as window handle this is quite impossible since
 * any reordering would invalidate window handles.
 * It's therefore better to use a linked list. Memory addresses are the window handles
 * and the list is ordered by z.order. */

/* By SR 
 * because VC++ doesn't know those routines and structs
 * remove them when you get the file back */
#pragma warning(disable: 4312)
#define far
typedef unsigned char byte;
typedef unsigned short word;
union REGS
{
	struct {
		byte al;
		byte ah;
	} h;
};
void int86(word intnum, const union REGS *in, union REGS *out);
byte inportb(word port);

#define MAXWINDOWS 64
/* The maximum size of the window caption including '\0' */
#define MAX_WINDOW_CAPTION 256
#ifndef NULL
#define NULL (void*)0
#endif

/* NOTES - Non-ACTIVE windows are DESTROYED EVERY GUI draw loop */
#define WMFLAGS_ACTIVE          0x00000001L     /* Window is created? */
#define WMFLAGS_TRANSPARENT     0x00000002L     /* Is Transparent other than graphic? */
#define WMFLAGS_FTOP            0x00000004L     /* "Forever Topmost"? */
#define WMFLAGS_DISABLE         0x00000008L     /* Is Disabled? */
#define WMFLAGS_MAXBOX          0x00000010L     /* Maximize box exists? */
#define WMFLAGS_MAXDISABLE      0x00000020L     /* Maximize button is not-clickable? */
#define WMFLAGS_MAXIMIZE        0x00000040L     /* Is Maximized? */
#define WMFLAGS_MINBOX          0x00000080L     /* Minimixe Box exists? */
#define WMFLAGS_MINDISABLE      0x00000100L     /* Minimize button is not-clickable? */
#define WMFLAGS_MINIMIZED       0x00000200L     /* Is Minimized? */
#define WMFLAGS_PINBOX          0x00000400L     /* Pin box exists? */
#define WMFLAGS_PINDISABLE      0x00000800L     /* Pin button is not-clickable? */
#define WMFLAGS_PINNED          0x00001000L     /* Pinned to VDesk? */
#define WMFLAGS_XDISABLE        0x00002000L     /* Does clicking close do nothing? */
#define WMFLAGS_RESIZABLE       0x00004000L     /* Is Resizable? */
#define WMFLAGS_TITLE           0x00008000L     /* Titlebar? */
#define WMFLAGS_BORDER          0x00010000L     /* Border? */
/* #define WMFLAGS_TOOLBOX */

/* Reserved flags are not to be used... */
#define WMFLAGS_RES1            0x00020000L
#define WMFLAGS_RES2            0x00040000L
#define WMFLAGS_RES3            0x00080000L
#define WMFLAGS_RES4            0x00100000L
#define WMFLAGS_RES5            0x00200000L     /* RESERVED */
#define WMFLAGS_RES6            0x00400000L     /* RESERVED */
#define WMFLAGS_RES7            0x00800000L     /* RESERVED */
#define WMFLAGS_RES8            0x01000000L     /* RESERVED */
#define WMFLAGS_RES9            0x02000000L     /* RESERVED */
#define WMFLAGS_RES10           0x04000000L     /* RESERVED */
#define WMFLAGS_RES11           0x08000000L     /* RESERVED */
#define WMFLAGS_RES12           0x10000000L     /* RESERVED */
#define WMFLAGS_RES13           0x20000000L     /* RESERVED */
#define WMFLAGS_RES14           0x40000000L     /* RESERVED */
#define WMFLAGS_RES15           0x80000000L     /* RESERVED */

/* Normal window's flags with border, titlebar, all boxes, the works... */
#define WMFLAGS_PDEF_NORMAL (WMFLAGS_ACTIVE | WMFLAGS_MAXBOX | WMFLAGS_MINBOX |     \
                             WMFLAGS_PINBOX | WMFLAGS_PINNED | WMFLAGS_RESIZABLE |  \
                             WMFLAGS_TITLE | WMFLAGS_BORDER)

/* Real mode memory pointer. PowerC is junk and requires
*  that the "far" keyword is there ---> Stupid */
unsigned char far *VGA = (unsigned char far *) 0xA0000000L;
unsigned char far *dblbuff;

struct RECT
{
    unsigned short left;
    unsigned short top;
    unsigned short width; 
    unsigned short height;
};         

struct WINDOW
{
    unsigned short x;
    unsigned short y;
    unsigned short width;
    unsigned short height;
	/* This should not be a pointer. It will only cause problems. */
    unsigned char caption[MAX_WINDOW_CAPTION];
    unsigned short zorder;      /* From 0 - MAXWINDOWS */
    unsigned long flags;        /* Use WMFLAGS_xxxx to set flags */
	/* This member doesn't make sense anymore. You shouldn't have a WINDOW struct
	 * anywhere except for the linked list. */
/*    unsigned char handle;*/       /* From 0 - MAXWINDOWS */
};

/* By SR ************************************************************************
 * A linked list to replace the window array.
 * Has the additional advantage of easily breaking the MAXWINDOWS limitation. */

struct _llistNode
{
	struct _llistNode *prev;
	struct WINDOW data;
	struct _llistNode *next;
};
typedef struct _llistNode llist_node;

struct _llistMain
{
	llist_node *head;
	llist_node *tail;
	unsigned int size;
};
typedef struct _llistMain llist;

void /*__fastcall*/ InitLList(llist *ll)
{
	ll->size = 0;
	ll->head = ll->tail = NULL;
}

/* warning: this function is likely to create memory leaks if WINDOW ever contains
 * dynamically allocated data */
/* Does PowerC know the __fastcall convention? If yes use it, it will speed things up. */
void /*__fastcall*/ ClearLList(llist *ll)
{
	register llist_node *q, *p = ll->head;
	while(p != NULL)
	{
		q = p->next;
		free(p);
		p = q;
	}
	InitLList(ll);
}

llist_node * InsertNode(llist *ll, llist_node *at, const struct WINDOW *pData)
{
	llist_node *newnode, *after;
	if(!ll || !at || !pData)
		return NULL;
	newnode = malloc(sizeof(llist_node));
	if(newnode == NULL)
		return NULL;
	++(ll->size);
	after = at->next;
	at->next = newnode;
	if(after)
		after->prev = newnode;
	else
		ll->tail = newnode;
	newnode->prev = at;
	newnode->next = after;
	newnode->data = *pData;
	return newnode;
}

llist_node * AddNodeAtHead(llist *ll, const struct WINDOW *pData)
{
	llist_node *newnode, *curhead;
	if(!ll || !pData)
		return NULL;
	newnode = malloc(sizeof(llist_node));
	if(!newnode)
		return NULL;
	++(ll->size);
	curhead = ll->head;
	if(curhead)
		curhead->prev = newnode;
	else
		ll->tail = newnode;
	ll->head = newnode;
	newnode->prev = NULL;
	newnode->next = curhead;
	newnode->data = *pData;
	return newnode;
}

void DeleteNode(llist *ll, llist_node *at)
{
	if(!ll || !at)
		return;
	--(ll->size);
	if(at->prev)
		at->prev->next = at->next;
	else
		ll->head = at->next;
	if(at->next)
		at->next->prev = at->prev;
	else
		ll->tail = at->prev;
	free(at);
}

/* The actual windows are stored here: */
llist wm_elements;
/********************************************************************************/

/* By SR
 * Since I use the addresses of the nodes as window handles and I
 * don't want to write llist_node far * every time it makes sense
 * to do a typedef */
typedef llist_node far * window_handle;

/****************************************************/
/* LOW-LEVEL ROUTINES - vgaintaction (mode);        */
/****************************************************/
void vgaintaction (unsigned char mode)
{
    union REGS i;
    i.h.ah = 0x00;
    i.h.al = mode;
    int86 (0x10, &i, &i);
}

/****************************************************/
/* DRAW PRIMITIVES - setpixel (x,y,c);              */
/* char getpixel(x,y);  drawrect(x,y,x2,y2,c);      */
/****************************************************/
void setpixel (unsigned short x, unsigned short y, unsigned char color)
{
    VGA[(y << 8) + (y << 6) + x] = color; 
/*    dblbuff[(y << 8) + (y << 6 ) + x] = color; */
}

unsigned char getpixel (unsigned short x, unsigned short y)
{
    return VGA[(y << 8) + (y << 6) + x];
}

void drawrect(unsigned short x, unsigned short y, unsigned short x2, unsigned short y2, unsigned char color)
{
    unsigned short tx, ty;
    for (ty = y; ty < y2; ty++)
        for (tx = x; tx < x2; tx++)
            setpixel (tx, ty, color);
}

void wipescrn()
{
    memset(VGA, 0, 0xffff);
}

/****************************************************/
/* DOUBLE BUFFER ROUTINES - int init_dblbuff(x,y);  */
/****************************************************/
int init_dblbuff(int x, int y)
{
    dblbuff = (unsigned char *) malloc (x * y);
    if (dblbuff == (void *) 0)
    {
		/* stdio.h not included... */
        /*printf ("\n-=- Unable to initialize double buffer -=-");*/
        return 0;
    }
    return 1;
}

void draw_dblbuff()
{
    #ifdef VERTICAL_RETRACE
        while ((inportb(0x03DA) & 0x08));
        while (!(inportb(0x03DA) & 0x08));
    #endif

    memcpy (dblbuff, VGA, 320 * 200);
}

/****************************************************/
/* CLIPPED DRAWING ROUTINES - clippixel(RECT,x,y,c);*/
/* cliprect(RECT,l,t,w,h,c);                        */
/****************************************************/
void clippixel(struct RECT area, unsigned short x, unsigned short y, unsigned char color)
{
    if ((x >= area.left) && (x < (area.left + area.width)))
        if ((y >= area.top) && (y < (area.top + area.height)))
            setpixel (x, y, color);
}

void cliprect(struct RECT area, unsigned short left, unsigned short top, unsigned short width, unsigned short height, unsigned char color)
{
    unsigned short tx, ty;
    /*unsigned short tx2, ty2;*/ /* unreferenced */

    for (ty = top; ty <= top + height; ty++)
        for (tx = left; tx <= left + width; tx++)

            if ((tx >= area.left) && (tx < (area.left + area.width)))
                if ((ty >= area.top) && (ty < (area.top + area.height)))
                    setpixel (tx, ty, color);

}   

/****************************************************/
/* DRAW A FUNKY BOX - funkybox(x1,y1,x2,y2);        */
/****************************************************/
void windowbox(int x1, int y1, int x2, int y2)
{
    int i, j;
    for (i = x1 + 1; i <= x2 - 2; i++)
    {
	/* Fill-in */
        for (j = y1 + 1; j <= y2 - 2; j++)
	{
	    setpixel (i, j, 24);
	}
    }

    for (i = x1; i <= x2 - 2; i++)
    {
	setpixel (i, y1, 26);
	if (i % 2 == 0)
	{
	    setpixel (i, y1 + 1, 26);
            setpixel (i + 1, y2 - 3, 22);
	}
	else
	{
	    setpixel (i, y1 + 2, 26);
            setpixel (i + 1, y2 - 2, 22);
	}
        setpixel (i + 1, y2 - 1, 22);
    }

    for (j = y1 + 1; j <= y2 - 2; j++)
    {
	setpixel (x1, j, 26);
	if (j % 2 == 0)
	{
	    setpixel (x1 + 1, j, 26);
            setpixel (x2 - 3, j + 1, 22);
	}
	else
	{
	    setpixel (x1 + 2, j, 26);
            setpixel (x2 - 2, j + 1, 22);
	}
        setpixel (x2 - 1, j, 22);
    }

    /* Now to do some touch-ups */
    setpixel (x2 - 1, y1, 22);
    setpixel (x2 - 3, y1 + 1, 22);
    setpixel (x1, y2 - 1, 26);
}

/****************************************************/
/* WINDOW MANAGER - initwm(); deinitwm(); drawgui();*/
/****************************************************/
void initwm()
{
	InitLList(&wm_elements);
}   

void deinitwm()
{
	ClearLList(&wm_elements);
}

/* Called once per loop... redraws the entire gui
*  09/10/02: BRAN: Optimized by doing flags check before zorder check */
/* Loop re-written by SR for the linked list */
void drawgui()
{
    window_handle wh;

    /* Main redraw loop... */
	/* Thanks to ordering we can remove one loop */
	/* head of the list has highest z-order */
    for (wh = wm_elements.head; wh != NULL; wh = wh->next)
    {                   
		/* We only do stuff with active windows... */
		/* Note by SR:
		 * I'm not sure what the active flag really means, but if my guess that it
		 * marked empty slots in the array is right you can remove this test too. 
		 * The linked list only contains active windows in the first place. */
		if (wh->data.flags & WMFLAGS_ACTIVE)
		{
			if (wh->data.flags & WMFLAGS_BORDER)
				windowbox(wh->data.x, wh->data.y, (wh->data.x + wh->data.width), (wh->data.y + wh->data.height));
			else
				drawrect(wh->data.x, wh->data.y, (wh->data.x + wh->data.width), (wh->data.y + wh->data.height), 24);
		}
    }
}

/* 14/10/02: Optimized by Curufir of MEGA-TOKYO FORUM */
/* SR: This is the function that suffers most because it must reorder the
 * nodes.
 * Completly rewritten. */
void movefrontwin(window_handle handle)
{
	window_handle h;
	if(!handle->next)
		return;	/* Already topmost */
	for(h = handle->next; h != NULL; h = h->next)
	{
		++(h->data.zorder);
	}
	if(handle->prev)
		handle->prev->next = handle->next;
	else
		wm_elements.head = handle->next;
	handle->next->prev = handle->prev;
	wm_elements.tail->next = handle;
	handle->prev = wm_elements.tail;
	handle->next = NULL;
	wm_elements.tail = handle;
	handle->data.zorder = 0;
}

/* Completly removed. Unnecessary for linked list. */
/* unsigned char findfreewin() */

/* Adapted to ll */
window_handle inwhatwin(int x, int y)
{
	window_handle h;

	/* traverse in back order */
	for (h = wm_elements.tail; h != NULL; h = h->prev)
	{
		/* SR: Again you can probably remove this */
		if (h->data.flags & WMFLAGS_ACTIVE)
		{
			if ((x >= h->data.x) && (x < (h->data.x + h->data.width)))
				if ((y >= h->data.y) && (y < (h->data.y + h->data.height)))
					return h;
		}
	}

    return NULL;
}

/***********************************************************/
/* WINDOW STUFF - window_handle createwin(x,y,w,h,txt,f);  */
/* void destroywin(h);                                     */
/*    NOTE: These ALL redraw the GUI nicely for us.        */
/***********************************************************/
window_handle createwin(int x, int y, int width, int height, unsigned char *caption, int flags)
{
	struct WINDOW data;
	window_handle h;
	unsigned char *p, *q;

    data.x = x;
    data.y = y;
    data.width = width;
    data.height = height;
    /* short version of strcpy */
	for(p=caption,q=data.caption; *p && p-caption < 256; ++p, ++q)
		*q = *p;
	*q = 0;
    data.flags = flags;
	data.zorder = 0;

	for(h=wm_elements.head; h != NULL; h = h->next)
		++(h->data.zorder);

	h = InsertNode(&wm_elements, wm_elements.tail, &data);

    drawgui();
    return h;
}

/* Plain and simple... Banners the window as un-active
*  with no features, now it's not drawn by the main draw loop. */
/* SR: not that simple anymore. Need to remove from list and adjust Zs */
void destroywin(window_handle handle)
{
	window_handle h;
	for(h = wm_elements.head; h != handle; h = h->next)
		--(h->data.zorder);
	DeleteNode(&wm_elements, handle);
	drawgui();
}

/* Move a window to a new x and nex y location. Automagically redraws */
int movewin(window_handle handle, int nx, int ny)
{
    handle->data.x = nx;
    handle->data.y = ny;
    drawgui();
    return 1;
}

/* Resize a window to new width and height. Automagically redraw s*/
int resizewin(window_handle handle, int nwidth, int nheight)
{
    if (handle->data.flags & WMFLAGS_RESIZABLE)
    {
        handle->data.width = nwidth;
        handle->data.height = nheight;
        drawgui();
        return 1;
    }
    return 0;
}

/* Completly removed. Easy access to the window through the use of the handle. */
/*struct WINDOW getwin(unsigned char handle)*/

int istopwin(window_handle handle)
{
	/* shortcut, does the same */
    return (handle->data.zorder == 0);
}

/****************************************************/
/* APPLICATION - main();                            */
/****************************************************/
void main ()
{
    unsigned char key;
    unsigned char temp;
    unsigned long i;
    int y = 0;
    window_handle hndl;
    unsigned int tx = 190, ty = 10;
    unsigned int otx = tx, oty = ty;
    unsigned int wtx, wty;

    struct RECT r1;
	/* It is rather useless to have a window struct ever on the stack
	 * they should exist only in the linked list */
    window_handle w1;
    window_handle w2;
    window_handle w3;
	
	r1.top = 100;       r1.left = 100;
    r1.height = 100;    r1.width = 100;

    if (init_dblbuff(320,200) != 1)
    {
        getch();
        return;
    }

	initwm();

    vgaintaction (0x13);

	/* I guess these are the one-time tests */
    drawrect (150, 50, 200, 100, 2);

    cliprect (r1, 40, 50, 120, 120, 8);
    setpixel (160, 170, 14);
    windowbox (40, 50, 100, 100);

    setpixel (100, 100, 7);
    setpixel (150, 150, 15);

    clippixel (r1, 120, 150, 4);
	// until here

    w1 = createwin (200,10,30,20,"Window 1", WMFLAGS_PDEF_NORMAL);
    w2 = createwin (210,20,30,20,"Window 2", WMFLAGS_PDEF_NORMAL);
    w3 = createwin (220,30,30,20,"Window 3", WMFLAGS_PDEF_NORMAL);

    drawgui();

    do
    {
        key = getch();

        if (key == 80)
            ty++;
        else if (key == 72)
            ty--;
        else if (key == 75)
            tx--;
        else if (key == 77)
            tx++;
        else if (key == 32)
        {
            hndl = inwhatwin(tx,ty);

            if (hndl != NULL)
            {

                if (istopwin(hndl) == 0)
                {                
                    movefrontwin(hndl);
                    drawgui();
                }
                else
                {
                    if (y == 0)
                        y = 1;
                    else
                    {
                        y = 0;
                    }
					
					/* Changes for linked list */
					wtx = hndl->data.x - tx;
                    wty = hndl->data.y - ty;
                }
            }
        }
        if (y == 1)
        {
            for(i = 0; i < 0xffff; i++)
            {
                VGA[i] = 0;
            }

            movewin(hndl, tx + wtx, ty + wty);            
        }

        setpixel(otx,oty,temp); /* warning: first use is before initializing */
        temp = getpixel(tx,ty);
        setpixel(tx,ty,15);
        otx = tx; oty = ty;
    }
    while (key != 27);

    getch();

    movefrontwin(w1);
    drawgui();

    getch ();
    vgaintaction (0x03);
    free(dblbuff);
	deinitwm();
}
