/* $Id: Exp $
***************************************************************************

   X server for LibGLTEX - xlgtex datatypes and related functions

   Copyright (C) 2002 Christopher Alexander North-Keys
                      http://www.talisman.org/~erlkonig/

***************************************************************************
*/

/* The key function in the file is InitOutput,
 * called by main in dix/main.c during server startup
 */

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/user.h>

#include "xgltex.h"

xgltexVars_t xgltexVars = {
	0,							/* in GCC we'd use ".initted = 0;" */
};

const xgltexArgument_t xgltexArgInfo[] = {
	{   /* long     short arg? fmt    scanf-target */
		"--xsize",    "-x", 1, "%lu", &xgltexVars.cmd.xsize,
		"X size (width)  in pixels of the texture"
	},{
		"--ysize",    "-y", 1, "%lu", &xgltexVars.cmd.ysize,
		"Y size (height) in pixels of the texture"
	},{
		"--zsize",    "-z", 1, "%lu", &xgltexVars.cmd.zsize,
		"Z size (depth)  in components of a pixel, from { 1 2 3 4 }"
	},{
		"--compbits", "-c", 1, "%u",  &xgltexVars.cmd.comp_bits,
		"number of bits in a component,   from { 1 8 16 32 }"
	},{
		"--pixbits",  "-p", 1, "%u",  &xgltexVars.cmd.pixel_bits,
		"bits to which a pixel is padded, from { 1 8 16 24 32 48 64 }"
	},{
		"--lsb",      "-l", 0,   0,   &xgltexVars.cmd.comp_lsb,
		"indicates that components must be in little-endian mode"
	},{
		"--shmid",    "-s", 1, "%u",  &xgltexVars.cmd.shmid,
		"pre-existing shared memory id to use      (conflicts w/ -b)"
	},{
		"--shmbytes", "-b", 1, "%lu", &xgltexVars.cmd.shmbytes,
		"minimum size of a shmem segment to create (conflicts w/ -s)"
	}
};
enum { xgltexArgInfoCount = sizeof xgltexArgInfo / sizeof *xgltexArgInfo };

/* 'finalize tells whether resources are actual and should be shut down */
void xgltexVarsTexClear(xgltexTexture_t *tex, int finalize)
{
	if(finalize)
	{
		if(tex->shmid && (XGLTEX_RESREQ_SHMGET == tex->resource))
			; /* use shmctl to mark for removal */
		if(tex->shmid && (XGLTEX_RESREQ_SHMAT  == tex->resource))
			; /* nothing special */
		if(tex->shmaddr)
		{
			shmdt(tex->shmaddr);
		}
	}
	/* STOMP */
	memset(tex, 0, sizeof *tex);
}

void xgltexVarsClear()  /* deallocates resources in use */
{
	xgltexVarsTexClear(&xgltexVars.tex, 1);  /* finalize the tex in use */
	xgltexVarsTexClear(&xgltexVars.cmd, 0); /* don't finalize - cmdline */
	xgltexVars.initted = 0;
}

int intInSet(int sought, int *set, int set_elem_count)
{
	int succp = 0;
	int i;
	for(i = 0 ; i < set_elem_count ; ++i)
		if(sought == set[i])
		{
			succp = 1;
			break;
		}
	return succp;
}

int xgltexVarsActivate()  /* setup .tex from .cmd */
{
	int succp = 1;			/* until anything disagrees */
	xgltexTexture_t *tex = &(xgltexVars.tex);
	xgltexTexture_t *cmd = &(xgltexVars.cmd);
	int zsize_valid[]      = { 1, 2, 3, 4 };
	int comp_bits_valid[]  = { 1, 8, 16, 32 };
	int pixel_bits_valid[] = { 1, 8, 16, 24, 32, 48, 64 };
	int booleans_valid[]   = { 0, 1 };
	enum { zsize_vsz     = sizeof zsize_valid      /sizeof zsize_valid[0],
		   comp_bits_vsz = sizeof comp_bits_valid  /sizeof comp_bits_valid[0],
		   pixel_bits_vsz= sizeof pixel_bits_valid /sizeof pixel_bits_valid[0],
		   booleans_vsz  = 2,	/* computing this would be amusing, but... */
	};

	/* clear anything currently in .tex */
	xgltexVarsTexClear(tex, 1);  /* finalize the tex in use */
	/* clone .cmd to .tex */
	memcpy(tex, cmd, sizeof (xgltexTexture_t));

	/* do any defaulting and complaining */
	tex->alloc_fail = 0;		/* start w/ the ones immune to cmdline */
	tex->image      = 0;
	tex->shmaddr    = 0;
	tex->resource   = XGLTEX_RESREQ_NONE;
	tex->image_size = 0;

	if(tex->xsize      < 1) tex->xsize = 1;
	if(tex->ysize      < 1) tex->ysize = 1;
	if(tex->zsize      < 1) tex->zsize = 1;
	if(tex->comp_bits  < 1) tex->comp_bits = 8; /* default to 1 byte comp's */
	if(tex->pixel_bits < 1) tex->pixel_bits = 24; /* default to 3B RGB */

	if((tex->pixel_bits) < (tex->zsize * tex->comp_bits))
		tex->pixel_bits = (tex->zsize * tex->comp_bits);

	if( 1 == tex->pixel_bits)   /* packed monochrome */
		tex->comp_bits = 1;		/* has to also be true */

	if(   intInSet(tex->zsize,      &zsize_valid[0],     zsize_vsz)
	   && intInSet(tex->comp_bits,  &comp_bits_valid[0],  comp_bits_vsz)
	   && intInSet(tex->pixel_bits, &pixel_bits_valid[0], pixel_bits_vsz)
	   && intInSet(tex->comp_lsb,   &booleans_valid[0],   booleans_vsz))
	{
		if(1 == tex->pixel_bits)  /* 8 pixel/bytes, pad scanlines to byte */
			tex->image_size = tex->xsize * (tex->ysize + 7)/8;
		else
			tex->image_size = tex->xsize * tex->ysize * (tex->pixel_bits/8);

		if(tex->shmid)
		{
			tex->resource = XGLTEX_RESREQ_SHMAT;
			tex->shmaddr  = shmat(tex->shmid, (const void*)0, 0);
			/* get size with shmctl() or something */
		} else {
			key_t key = IPC_PRIVATE;                /* key_t is int */
			int shmflag = 0;
			tex->resource = XGLTEX_RESREQ_SHMGET;   /* else defaults ..NONE */
			if( ! tex->shmbytes)
				tex->shmbytes = ((tex->image_size / PAGE_SIZE) +1) * PAGE_SIZE;
			tex->shmid   = shmget(key, tex->shmbytes, IPC_CREAT | 0666);
			tex->shmaddr = shmat(tex->shmid, (const void*)0, shmflag);
		}
		if(tex->shmaddr)
			tex->image = tex->shmaddr;
	} else {
		ErrorF("illegal value in zsize, comp_bits, pixel_bits or comp_lsb\n");
		succp = 0;
	}

	ErrorF("+++++++++++\n");
	xgltexTestPattern(stderr);
	xgltexVarsPrint(stderr);

	/* xscreen still needs to be initialized */
	return succp;
}

unsigned long int numberFromBytes(void *src, size_t count, int bigendian_p)
{
	unsigned long int number = 0;
	if(bigendian_p)
	{
		unsigned char *current = (unsigned char *)src;
		unsigned char *toofar = current + count;
		while(current < toofar)
		{
			number <<= 8;
			number |= *current;
			++current;
		}
	} else {
		unsigned char *srcbyte = (unsigned char *)src;
		unsigned char *toofar  = srcbyte + 1;
		unsigned char *current = srcbyte - (count - 1);
		while(current > toofar)
		{
			number <<= 8;
			number |= *current;
			--current;
		}
	}
	return number;
}


// large-order bytes are done first, if 'forward, write big-endian data
// write count bytes into bytes starting at trg.
//    if count is >4, pad with zeros
//    if count is <4, skip high-order section of number
void numberToBytes(void *trg, size_t count, unsigned long int number,
				   int bigendian_p)
{
	if(bigendian_p)
	{
		// write modulo'd-off values starting at right 
		unsigned char *trgbyte = (unsigned char *)trg;
		unsigned char *current = trgbyte + (count - 1);
		unsigned char *toofar  = trgbyte - 1;
		while(current > toofar)
		{
			unsigned char fragment = number % 256;
			*current = fragment;
			number >>= 8;
			--current;
		}
	} else {
		// write modulo'd-off values starting at left (little-endian)
		unsigned char *trgbyte = (unsigned char *)trg;
		unsigned char *current = trgbyte;
		unsigned char *toofar  = trgbyte + count;
		while(current < toofar)
		{
			unsigned char fragment = number % 256;
			*current = fragment;
			number >>= 8;
			++current;
		}
	}
}

int xgltexSetPixel(unsigned int x, unsigned int y,
				   float r, float g, float b, float a)
{
	int succp = 0;
	xgltexTexture_t *tex = &(xgltexVars.tex);
#if 0
	if((x < 7) && (y < 7))
		fprintf(stderr, __FUNCTION__
				": %d,%d (%.3f %.3f %.3f %.3f)\n",
				x, y, r, g, b, a);
#endif
	/* SGI RGB images and texture are 0,0 in bottom left (sci style) and   */
	/* by scanline.  so the first scanline pixels are lowest in the vector */
	if((x <= tex->xsize) && (y <= tex->ysize)) 
	{
		if( 1 == tex->pixel_bits) /* also implies ( 1 == tex->comp_bits) */
		{
			int xsize_in_bytes = (tex->xsize + 7) / 8;
			unsigned char *target = tex->image + (  (y * xsize_in_bytes)
												  + ((x + 7) / 8));
			int target_bit = x % 8;	/* [0] is left bit */
			int bitmask = 1 << (7 - target_bit); /* left-to-right numbering */
			if( !! r)    /* get red component, permuted to a single bit */
				*target |=  bitmask; /* enable new bit pixel */
			else
				*target &= ~bitmask; /* clear old bit pixel */
		}
		else					/* byte or larger components */
		{   /* we might use floorf, by the one on this host is broken */
			float rf = 255.98 * r;
			float gf = 255.98 * g;
			float bf = 255.98 * b;
			float af = 255.98 * a;
#if 0
			int t3 = (x<7&&y<7)
				? fprintf(stderr, "                    "
						  "(%.1f %.1f %.1f %.1f) rf gf bf af\n",
						  rf, gf, bf, af) : 0;	
#endif
			unsigned char rr = (unsigned char)rf;
			unsigned char gg = (unsigned char)gf;
			unsigned char bb = (unsigned char)bf;
			unsigned char aa = (unsigned char)af;
#if 0
			int t2 = (x<7&&y<7)
				? fprintf(stderr, "                    "
						  "(%5d %5d %5d %5d) rr gg bb aa\n",
						  rr, gg, bb, aa) : 0;
#endif
			int pixel_bytesize = tex->pixel_bits / 8;
			unsigned char *pixel
				= tex->image + (  (y * tex->xsize * (pixel_bytesize))
								+ (x *     1      * (pixel_bytesize)));
			int comp_bytesize   = tex->comp_bits / 8;
			unsigned char *rc = pixel + (0 * comp_bytesize);
			unsigned char *gc = pixel + (1 * comp_bytesize);
			unsigned char *bc = pixel + (2 * comp_bytesize);
			unsigned char *ac = pixel + (3 * comp_bytesize);
#if 0
			int t1 = (x<7&&y<7)
				? fprintf(stderr, "                    "
						  " pixel %#lx (pBsz %d, cBsz %d)\n"
						  "                    "
						  " %#lx %#lx %#lx %#lx \n",
						  (unsigned long int)pixel,
						  pixel_bytesize,
						  comp_bytesize,
						  (unsigned long int)rc,
						  (unsigned long int)gc,
						  (unsigned long int)bc,
						  (unsigned long int)ac
						  ) : 0;
#endif
			// tex->zsize limits the number of components we need to process
			switch(tex->comp_bits)
			{
			case 1:		/* no 3bit pixels, so this must be monochrome
						 * true bitmaps already done, so this is 1 bit set
						 * at 1 byte alignment (eek).
						 * fall into (case 8:) (case/zsize 1) below - luminance
						 */
			case 8:
				switch(tex->zsize) {
				case 4: *ac = aa;  // rgba
				case 3: *bc = bb;  // rgb
				case 2: *gc = gg;  // lum+alpha
				case 1: *rc = rr;  // lum
					succp = 1;
					break;
				default: ErrorF(__FUNCTION__": bad depth %d\n", tex->zsize);
					break;
				}
				break;
			}
#if 0
			if((x < 7) && (y < 7))
				fprintf(stderr, "   result->         "
						"(%4d %4d %4d %4d)\n", *ac, *bc, *gc, *rc);
#endif

		}
	}
	return succp;
}

int xgltexSetRectangle(unsigned int x0, unsigned int y0,
					   unsigned int w,  unsigned int h,
					   float r, float g, float b, float a)
{
	int succp = 0;
	xgltexTexture_t *tex = &(xgltexVars.tex);
	if(   (0 <= x0) && (x0 <= tex->xsize)
	   && (0 <= y0) && (y0 <= tex->ysize)
	   && (0 <= w)  && ((x0 + w) <= tex->xsize)
	   && (0 <= h)  && ((y0 + h) <= tex->ysize))
	{
		int xmax = x0 + w, ymax = y0 + h;
		int x, y;
		for(    y = y0 ; y < ymax ; ++y)
			for(x = x0 ; x < xmax ; ++x)
				xgltexSetPixel(x, y, r, g, b, a);
		succp = 1;
	}
	return succp;
}

void xgltexTestPattern()
{
	xgltexTexture_t *tex = &(xgltexVars.tex);
	int period = 10;
	int thick = 3;
	int x, y;
	for(    y = 0 ; y < tex->ysize ; ++y)
		for(x = 0 ; x < tex->xsize ; ++x)
			xgltexSetPixel(x, y, 1.00, 0.5, 1.0, 1.0);
/*	xgltexSetRectangle(0, 0, tex->xsize, tex->ysize, 0, 0, 0, 0); */
	for(    y = thick ; y < (tex->ysize - thick); y += period)
		for(x = thick ; x < (tex->xsize - thick); x += period)
			xgltexSetRectangle(x, y, thick, thick, 0.0, 1.0, 0.0, 1.0);
}

const char *xgltexResourceToString(xgltexResource_t r)
{
	const char *s = "<unknown/error>";
	switch(r)
	{
	case XGLTEX_RESREQ_NONE:    s = "none";     break;
	case XGLTEX_RESREQ_SHMAT:   s = "shmat";    break;
	case XGLTEX_RESREQ_SHMGET:  s = "shmget";   break;
	case XGLTEX_RESREQ_INVALID: s = "invalid";  break;
	}
	return s;
}

void xgltexVarsTexPrint(FILE *out, xgltexTexture_t *tex)
{
	fprintf(out,
			"(width)  X: %d pixels\n"
			"(height) Y: %d pixels\n"
			"(depth)  Z: %d components       { 1 2 3 4 }\n",
			tex->xsize, tex->ysize, tex->zsize);
	fprintf(out, " comp bits: %d bits/component   { 1 8 16 32 }\n",
			tex->comp_bits);
	fprintf(out, "pixel bits: %d bits/pixel w/pad { 1 8 16 24 32 48 64 }\n",
			tex->pixel_bits);
	fprintf(out, "alloc fail: %s\n", (tex->alloc_fail ? "true" : "false"));
	fprintf(out, "little-end: %s\n", (tex->comp_lsb   ? "true" : "false"));
	fprintf(out, "image size: %d\n", tex->image_size);
	fprintf(out, "image addr: %#lx\n", (unsigned long int)(tex->image));
	fprintf(out, "  resource: %s\n", xgltexResourceToString(tex->resource));
	fprintf(out, "shared mem: %d shmid, %ld bytes, address %#lx\n",
			tex->shmid, tex->shmbytes,
			(unsigned long int)(tex->shmaddr));
	fprintf(out, "   xscreen: %#lx\n", (unsigned long int)tex->xscreen);
}

void xgltexVarsPrint(FILE *out)
{
	fprintf(out, "---------- defaults and command line overrides\n");
	xgltexVarsTexPrint(out, &(xgltexVars.cmd));
	fprintf(out, "---------- current texture settings\n");
	xgltexVarsTexPrint(out, &(xgltexVars.tex));
	fprintf(out, "---------- general variables\n");
	fprintf(out, "   initted: %s\n", (xgltexVars.initted ? "true" : "false"));
	fprintf(out, "---------- end\n");
}

void xgltexUsage(void)
{
	int i;
	ErrorF("\n");
	ErrorF("XGLTEX adds numerous options (both short and long form), most\n"
		   "       to allow configuration of the image format in memory:\n");
	ErrorF(" short/long   arg  description\n");
	ErrorF("------------  ---  -------------------------------------------\n");
	for( i = 0 ; i < xgltexArgInfoCount ; ++i)
	{
		ErrorF("%s %-10s %3s  %s\n",
			   xgltexArgInfo[i].option_short,
			   xgltexArgInfo[i].option_long,
			   (xgltexArgInfo[i].optarg_required
				? "<n>"
				: "   "
				),
			   xgltexArgInfo[i].help);
	}
	ErrorF("\n");
}

int xgltexProcessArg(int argc, char *argv[], int argi)
{
	int args_processed = 0;  /* 1 for flag, 2 for opt + optarg */
	int i;
/*	fprintf(stderr, __FUNCTION__ " entered\n"); /**/
	for( i = 0 ; (i < xgltexArgInfoCount) && ! args_processed ; ++i)
	{
/*		fprintf(stderr, __FUNCTION__ " processing \"%s\"\n", argv[argi]);/**/
		if(   ( ! strcmp(argv[argi],      xgltexArgInfo[i].option_short))
		   || ( ! strcmp(argv[argi],      xgltexArgInfo[i].option_long)))
		{
			if(xgltexArgInfo[i].optarg_required) /* optarg needed */
			{
				if((argi + 1) < argc)           /* ... and present? */
				{
					if(1 == sscanf(argv[argi + 1],  /* successful parse? */
								   xgltexArgInfo[i].sscanf_format,
								   xgltexArgInfo[i].sscanf_target))
					{
						args_processed = 2;        /* parse succeeded */
/*						fprintf(stderr, "arg + optarg set\n"); /**/
					}
					else
						ErrorF("Option \"%s\" / \"%s\""  /* parse failed */
							   " parse of optarg \"%s\" failed\n",
							   xgltexArgInfo[i].option_short,
							   xgltexArgInfo[i].option_long,
							   argv[argi + 1]);
				} else {		                /* missing optarg */
					ErrorF("Option \"%s\" / \"%s\""
						   " requires an option-argument\n",
						   xgltexArgInfo[i].option_short,
						   xgltexArgInfo[i].option_long);
				}
			} else {                             /* no optarg required */
				*(unsigned int*)(xgltexArgInfo[i].sscanf_target) = 1;
/*				fprintf(stderr, "arg set\n");  /**/
				args_processed = 1;	             /* assumed successful */
			}
		}
	}
	return args_processed;
}

/*-------------------------------------------------------------- eof --- */

