/**********************************************/
/* 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>

#define MAXWINDOWS 64

/* 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;
    unsigned char *caption;
    unsigned short zorder;      /* From 0 - MAXWINDOWS */
    unsigned long flags;        /* Use WMFLAGS_xxxx to set flags */
    unsigned char handle;       /* From 0 - MAXWINDOWS */
};

/* Have have MAXWINDOWS number of windows in our GUI */
struct WINDOW wm_elements [MAXWINDOWS];

/****************************************************/
/* 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)
    {
        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;

    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(); drawgui();            */
/****************************************************/
void initwm()
{
    int i;

    /* This should be the only place that will EVER set a handle */
    for (i = 0; i < MAXWINDOWS; i++)
    {
        wm_elements[i].handle = i;
        wm_elements[i].flags = 0;
    }
}   

/* Called once per loop... redraws the entire gui
*  09/10/02: BRAN: Optimized by doing flags check before zorder check */
void drawgui()
{
    int tz;
    int i;

    /* Main redraw loop... */
    for (tz = MAXWINDOWS - 1; tz >= 0; tz--)
    {                   
        for (i = 0; i < MAXWINDOWS; i++)
        {   
            /* We only do stuff with active windows... */
            if (wm_elements[i].flags & WMFLAGS_ACTIVE)
            {
                if (wm_elements[i].zorder == tz)
                {
                    if (wm_elements[i].flags & WMFLAGS_BORDER)
                        windowbox(wm_elements[i].x, wm_elements[i].y, (wm_elements[i].x + wm_elements[i].width), (wm_elements[i].y + wm_elements[i].height));
                    else
                        drawrect(wm_elements[i].x, wm_elements[i].y, (wm_elements[i].x + wm_elements[i].width), (wm_elements[i].y + wm_elements[i].height), 24);
                }
            }
        }
    }
}

/* 14/10/02: Optimized by Curufir of MEGA-TOKYO FORUM */
void movefrontwin(unsigned char handle)
{
    int tz, i;
    unsigned char t_zorder = wm_elements[handle].zorder;

    for (i = 0; i < MAXWINDOWS; i++)
    {
        if (t_zorder > wm_elements[i].zorder)
        {
            wm_elements[i].zorder++;
        }
    }
    wm_elements[handle].zorder = 0;
}

unsigned char findfreewin()
{
    unsigned int i, t;

    for (i = 0; i <= MAXWINDOWS; i++)
    {
        /* If the window is not active... it's the one we want to allocate */
        if (!(wm_elements[i].flags & WMFLAGS_ACTIVE)) break;        
    }

    if (i == MAXWINDOWS) return 0xFF;
    else return i;
}

unsigned char inwhatwin(int x, int y)
{
    unsigned int tz;
    unsigned char i;

    for (tz = 0; tz < MAXWINDOWS; tz++)
    {
        for (i = 0; i < MAXWINDOWS; i++)
        {
            if (wm_elements[i].flags & WMFLAGS_ACTIVE)
            {
                if (wm_elements[i].zorder == tz)
                {         
                    if ((x >= wm_elements[i].x) && (x < (wm_elements[i].x + wm_elements[i].width)))
                        if ((y >= wm_elements[i].y) && (y < (wm_elements[i].y + wm_elements[i].height)))
                            return i;
                }
            }
        }
    }

    return 255;
}

/* 09/10/02: OPTIMIZED BY CURUFIR OF MEGA-TOKYO FORUM
unsigned char inwhatwin(int x, int y)
{
    unsigned int minz;
    unsigned int fi;
    unsigned char i;

    minz=MAXWINDOWS;
    fi=255;

    for (i = 0; i < MAXWINDOWS; i++)
    {
        if (wm_elements[i].flags & WMFLAGS_ACTIVE)
        {        
            if ((x >= wm_elements[i].x) && (x < (wm_elements[i].x + wm_elements[i].width))){
                if ((y >= wm_elements[i].y) && (y < (wm_elements[i].y + wm_elements[i].height))){
                    if(minz>wm_elements[i].zorder){
                        minz=wm_elements[i].zorder;
                        fi=i;
                        if(minz==0) return fi;
                    }
                }
            }
        }
    }
    return fi;
}*/

/****************************************************/
/* WINDOW STUFF - WINDOW createwin(x,y,w,h,txt,f);  */
/* void destroywin(h);                              */
/*    NOTE: These ALL redraw the GUI nicely for us. */
/****************************************************/
struct WINDOW createwin(int x, int y, int width, int height, unsigned char *caption, int flags)
{
    unsigned char handle;

    handle = findfreewin();
    if (handle == 0xFF) return 0;

    wm_elements[handle].x = x;
    wm_elements[handle].y = y;
    wm_elements[handle].width = width;
    wm_elements[handle].height = height;
    wm_elements[handle].caption = caption;
    wm_elements[handle].flags = flags;

    /* THIS LINE MAY NEED TO BE CHANGED!!! */
    wm_elements[handle].zorder = MAXWINDOWS - 1;

    movefrontwin(handle);
    drawgui();
    return wm_elements[handle];
}

/* Plain and simple... Banners the window as un-active
*  with no features, now it's not drawn by the main draw loop. */
void destroywin(unsigned char handle)
{
    wm_elements[handle].flags = 0;
    drawgui();
}

/* Move a window to a new x and nex y location. Automagically redraws */
int movewin(unsigned char handle, int nx, int ny)
{
    wm_elements[handle].x = nx;
    wm_elements[handle].y = ny;
    drawgui();
    return 1;
}

/* Resize a window to new width and height. Automagically redraw s*/
int resizewin(unsigned char handle, int nwidth, int nheight)
{
    if (wm_elements[handle].flags & WMFLAGS_RESIZABLE)
    {
        wm_elements[handle].width = nwidth;
        wm_elements[handle].height = nheight;
        drawgui();
        return 1;
    }
    return 0;
}

struct WINDOW getwin(unsigned char handle)
{
    return wm_elements[handle];
}

int istopwin(unsigned char handle)
{
    if (wm_elements[handle].zorder == 0)
        return 1;
    else
        return 0;
}

/****************************************************/
/* APPLICATION - main();                            */
/****************************************************/
void main ()
{
    unsigned char key;
    unsigned char temp;
    unsigned long i;
    int y = 0;
    unsigned char hndl;
    unsigned int tx = 190, ty = 10;
    unsigned int otx = tx, oty = ty;
    unsigned int wtx, wty;

    struct RECT r1;
    struct WINDOW w1;
    struct WINDOW w2;
    struct WINDOW w3;

    r1.top = 100;       r1.left = 100;
    r1.height = 100;    r1.width = 100;

    if (!(init_dblbuff(320,200) == 1))
    {
        getch();
        return;
    }

    vgaintaction (0x13);
    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);

    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 != 255)
            {

                if (istopwin(hndl) == 0)
                {                
                    movefrontwin(hndl);
                    drawgui();
                }
                else
                {
                    if (y == 0)
                        y = 1;
                    else
                    {
                        y = 0;
                    }

                    wtx = wm_elements[hndl].x - tx;
                    wty = wm_elements[hndl].y - ty;
                }
            }
        }
        if (y == 1)
        {
            for(i = 0; i < 0xffff; i++)
            {
                VGA[i] = 0;
            }

            movewin(hndl, tx + wtx, ty + wty);            
        }

        setpixel(otx,oty,temp);
        temp = getpixel(tx,ty);
        setpixel(tx,ty,15);
        otx = tx; oty = ty;
    }
    while (key != 27);

    getch();

    movefrontwin(w1.handle);
    drawgui();

    getch ();
    vgaintaction (0x03);
    free(dblbuff);
}
