#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <errno.h>
#include "getopt.h"

#include "grf/grf_types.h"
#include "grf/grf_bitmap.h"
#include "grf/grf_colormap.h"
#include "viewioctl.h"


int version = 1;
int revision = 0;


struct option lopts[] = {
    { "width", required_argument, NULL, 'W' },
    { "height", required_argument, NULL, 'H' },
    { "depth", required_argument, NULL, 'D' },
    { "help", no_argument, NULL, 'h' },
    { "version", no_argument, NULL, 'v' },
    { NULL, }
};

char *optstr = "W:H:D:hv";
char *help_string = "options: \n"
    "   [-hv] [-D DEPTH] [-H HEIGHT] [-W WIDTH]\n"
    "   [--depth=NUM_PLANES] [--height=PIXELS]\n"
    "   [--help] [--version] [--width=PIXELS]";

void
xioctl (int fdesc, int cmd, void *addr)
{
    if (ioctl (fdesc, cmd, addr)) {
        fprintf (stderr, "command: 0x%lx\n",cmd);
    	perror ("ioctl");
    	exit (1);
    }
}

void *
xmalloc (int b)
{
    void *m = (void *)malloc (b);
    if (m == NULL) {
        perror("xmalloc");
        exit (1);
    }
    return (m);
}

/* scan for available view to use. */
int
xopen_view (void)
{
    u_char buffer[13];
    int i, fd;
    
    for (i=0; i < 100; i++) {
	sprintf (buffer, "/dev/view%02d", i);
	printf ("trying \"%s\"...", buffer);
	fd = open (buffer, O_RDWR);
	if (fd < 0 && errno != EBUSY) {
	    printf ("failed.\n");
	    perror ("xopen_view()");
	    exit (1);
	} else if (fd < 0) {
	    printf ("used.\n");
	} else {
	    return (fd);
	}
    }
    fprintf (stderr, "ran out of views\n");
    exit (1);
}
	    

u_int depth, width, height;

int 
main (int argc, char **argv)
{
    bmap_t bm;
    int longind = 0, opt, cw = 0;
    int opt_quit = 0, val = 0;
    int len, i, tmp;
    caddr_t pd;
    colormap_t *cm;
    struct view_size vs;
    int vfd;

    vfd = xopen_view ();
    xioctl (vfd, VIEW_GETSIZE, &vs);
    printf ("[old: (w:%d h:%d d:%d)]\n", vs.width, vs.height, vs.depth);

    while (EOF != (opt = getopt_long (argc, argv, optstr, lopts, &longind))) {
	switch (opt) {
	  case 'W':
            val = atoi (optarg);
	    if (val) {
	        vs.width = val;
	        cw = 1;
	    } else {
	        fprintf (stderr, "%s: width: VAL !> 0\n");
	        exit (1);
	    }
	    break;
	  case 'H':
            val = atoi (optarg);
	    if (val) {
	        vs.height = val;
	        cw = 1;
	    } else {
	        fprintf (stderr, "%s: height: VAL !> 0\n");
	        exit (1);
	    }
	    break;
	  case 'D':
            val = atoi (optarg);
	    if (val) {
	        vs.depth = val;
	        cw = 1;
	    } else {
	        fprintf (stderr, "%s: depth: NUM_PLANES !> 0\n");
	        exit (1);
	    }
	    break;
	  case '?':
	  case 'h':
	    fprintf (stdout, "%s %d.%d\n", argv[0], version, revision); 
	    fprintf (stdout, "usage: %s [options]\n", argv[0]);
	    fprintf (stdout, "%s\n", help_string);
	    exit (0);
	  case 'v':
	    fprintf (stdout, "%s version %d.%d\n", argv[0], version, revision); 
	    exit (0);
	}
    }

    if (cw) {
    	xioctl (vfd, VIEW_SETSIZE, &vs);
    	printf ("[new: (w:%d h:%d d:%d)]\n", vs.width, vs.height, vs.depth);
    }
    
    sleep (1);
    xioctl (vfd, VIEW_GETBITMAP, &bm);
    printf ("bitmap defaults: width: %d  height: %d  depth: %d\n", 
            bm.bytes_per_row << 3, bm.rows, bm.depth);

    len = bm.bytes_per_row*bm.rows*bm.depth;

    pd = mmap (0, len, PROT_READ|PROT_WRITE, MAP_FILE, vfd, 0);
    if (pd == (caddr_t)-1) {
	printf ("mmap() unsuccessful\n");
	exit (1);
    }
    printf ("mapping data at 0x%lx\n", pd);


    /* now lets change the colors. */
    cm = xmalloc (sizeof (*cm) * sizeof (u_long) * (1u<<bm.depth));
    cm->entry = (u_long *)&cm[1];
    cm->first = 0;
    cm->size = 1u << bm.depth;

    xioctl (vfd, VIEW_GETCOLORMAP, cm); 

    printf ("this display supports %d color registers ", cm->size);
    printf ("of type \"%s\"\n",cm->type == CM_MONO ? "MONOCHROME" :
                                cm->type == CM_GREYSCALE ? "GREYSCALE" :
                                 "COLOR" /* CM_COLOR */);
    sleep (3);
    printf ("the current values for the registers are:");
    for (i = 0; i < cm->size; i++) {
        if (!(i%8)) {
           printf ("\n");
        }
        printf ("0x%08lx ", cm->entry[i]);
    }
    if (i%8) {
        printf ("\n");
    }
    sleep (3);

    /* lets fill the bitmap. */
    for (i = 0; i < bm.depth; i++) {
	int k;
	char *pl = pd + bm.bytes_per_row*bm.rows*i;
	for (k = 0; k < (bm.rows); k++) {
	    int j;
	    char *data = &pl[bm.bytes_per_row*k];
	    
	    for (j = 0; j < (bm.bytes_per_row); j ++) {
		data[j] = 0x00;
	    }
	}
    }
    sleep (2);

    if (cm->type == CM_COLOR) {
    	int mr = cm->red_mask;
    	int mg = cm->green_mask;
    	int mb = cm->blue_mask;
    	u_long old;
        printf ("we will now cycle all the possible primary colors.\n");
	
	cm->size = 1;		/* set the size of our color map to 1 */
	cm->first = 0;		/* our one entry is for register 0 */
	
	xioctl (vfd, VIEW_DISPLAY, 0);
	old = cm->entry[0];

        for (i = 0; i <= mr; i++) { 
            cm->entry[0] = MAKE_COLOR_ENTRY (i,0,0);
            xioctl (vfd, VIEW_USECOLORMAP, cm); 
        }
        for (i = 0; i <= mg; i++) { 
            cm->entry[0] = MAKE_COLOR_ENTRY (0,i,0);
            xioctl (vfd, VIEW_USECOLORMAP, cm); 
        }
        for (i = 0; i <= mb; i++) { 
            cm->entry[0] = MAKE_COLOR_ENTRY (0,0,i);
            xioctl (vfd, VIEW_USECOLORMAP, cm); 
        }
	xioctl (vfd, VIEW_REMOVE, 0);

        cm->entry[0] = old;
        xioctl (vfd, VIEW_USECOLORMAP, cm); 
	xioctl (vfd, VIEW_DISPLAY, 0);

    } else if (cm->type == CM_GREYSCALE) {
    	int mg = cm->grey_mask;
    	u_long old;
        printf ("we will now cycle all the possible greyscales.\n");

	cm->size = 1;		/* set the size of our color map to 1 */
	cm->first = 0;		/* our one entry is for register 0 */

	xioctl (vfd, VIEW_DISPLAY, 0);
	old = cm->entry[0];

        for (i = 0; i <= mg; i++) { 
            cm->entry[0] = MAKE_GREY_ENTRY (i);
            xioctl (vfd, VIEW_USECOLORMAP, cm); 
        }
	xioctl (vfd, VIEW_REMOVE, 0);

        cm->entry[0] = old;
        xioctl (vfd, VIEW_USECOLORMAP, cm); 
	xioctl (vfd, VIEW_DISPLAY, 0);

    } else if (cm->type == CM_MONO) {
        printf ("nothing to show you with a monochrome monitor.\n");
    }

    /* lets fill the bitmap. */
    tmp = 10;
    while (tmp--) {
	for (i = 0; i < bm.depth; i++) {
	    int k;
	    char *pl = pd + bm.bytes_per_row*bm.rows*i;
	    for (k = 0; k < (bm.rows); k+=2) {
		int j;
		char *data = &pl[bm.bytes_per_row*k];
		
		for (j = 0; j < (bm.bytes_per_row); j++) {
		    data[j] = 0xAA;
		}
		for (j = 0; j < (bm.bytes_per_row); j++) {
		    data[j] = 0x55;
		}
		for (j=0; j<tmp*100; j++)
		    ;
	    }
	    for (k = 1; k < (bm.rows); k+=2) {
		int j;
		char *data = &pl[bm.bytes_per_row*k];
		
		for (j = 0; j < (bm.bytes_per_row); j++) {
		    data[j] = 0xAA;
		}
		for (j = 0; j < (bm.bytes_per_row); j++) {
		    data[j] = 0x55;
		}
		for (j=0; j<tmp*100; j++)
		    ;
	    }
	}
	for (i = 0; i < bm.depth; i++) {
	    int k;
	    char *pl = pd + bm.bytes_per_row*bm.rows*i;
	    for (k = 0; k < (bm.rows); k++) {
		int j;
		char *data = &pl[bm.bytes_per_row*k];
		
		for (j = 0; j < (bm.bytes_per_row); j++) {
		    data[j] = 0x00;
		}
		for (j=0; j<tmp*100; j++)
		    ;
	    }
	}
    }

    munmap (pd, len);
    close (vfd);

    printf ("...closed\n");
    return (0);
}

