#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/types.h>

#include <X11/Xlib.h>
#include <X11/keysym.h>

#include <pict.h>

#ifdef USEJPEG
#define HAVE_JPEG
#endif

#ifdef	HAVE_JPEG
#include "jpeglib.h"
#include "jerror.h"
#endif

static	char SCSIident[] = "@(#) xdp 2.0, (c) J.A. Magallon, 07/06/95";

#ifndef	DEFCOLS
#define	DEFCOLS	128
#endif

#if defined(__STDC__) || defined (__stdc__)
void	scalePict(Pict,double,double);
Pict	adjustPict(Pict,Pict,int,int,double,double);
int		setXColormap(Pict);
#ifdef	HAVE_JPEG
void	writeJPEG(Pict,char *,int);
#endif
#else
void	scalePict();
Pict	adjustPict();
int		setXColormap();
#ifdef	HAVE_JPEG
void	writeJPEG();
#endif
#endif

Display		*theDisplay;
int			theScreen;
Window		theRootWindow;
Visual		*theVisual;
GC			theGC;
Colormap	theDefaultColormap;
Window		theWindow;
XImage		*theImage;

int		mode = OPTIMAL,ncols=DEFCOLS,quality=85;
double	sx = 1,sy = 1;
int		fixedw = 0,fixedh = 0;
int		show = 0,pause = 0;

#if defined(__STDC__) || defined (__stdc__)
main (
int	argc,
char	*argv[]
)
#else
main (argc, argv)
int	argc;
char	*argv[];
#endif
{
	extern char	*optarg;
	extern int	optind,opterr;
	int		option;

	int		ever = 1,redraw,first,current,newcurrent,reload;
	KeySym	key;
	unsigned char *data;
	char	*name,title[256];
	Pict	orgPict,dispPict;

	XEvent			event;
	XExposeEvent	*exp_event;

	while ( (option=getopt(argc,argv,"agc:s:x:y:w:h:p:q:")) != EOF )
	{
		switch(option)
		{
			case 'a':	mode = M_ALPHA;
						break;
			case 'g':	mode = GRAYSCALE;
						break;
			case 'c':	ncols = atoi(optarg);
						if (ncols < 2) ncols=2;
						break;
			case 'q':	quality = atoi(optarg);
						if (quality < 5) quality=5;
						if (quality > 100) quality=100;
						break;
			case 'x':	sx = atof(optarg);
						if (sx < 0) sx=1;
						break;
			case 'y':	sy = atof(optarg);
						if (sy < 0) sy=1;
						break;
			case 'w':	fixedw = atoi(optarg);
						if (fixedw < 0) fixedw=0;
						break;
			case 'h':	fixedh = atoi(optarg);
						if (fixedh < 0) fixedh=0;
						break;
			case 's':	sx = atof(optarg);
						if (sx < 0) sx=1;
						sy = sx;
						break;
			case 'p':	pause = atoi(optarg);
						show = 1;
						break;
			default:
						fprintf(stderr,
		"Uso: %s [a|g][c N][s S][x Sx][y Sy][w Rx][h Ry][p pause] [file]\n",
						argv[0]);
						exit(1);
						break;
		}
	}

	first  = optind;
	current = first;

	orgPict = pict_read_pict(argv[current]);
	if (fixedw >0) sx = fixedw/(double)pict_width(orgPict);
	if (fixedh >0) sy = fixedh/(double)pict_height(orgPict);
	name = (argv[current]==NULL ? "stdin" : argv[current]);

	dispPict = NULL;
	dispPict = adjustPict(orgPict,dispPict,mode,ncols,sx,sy);

	if ((theDisplay = XOpenDisplay(NULL)) == NULL )
	{
		printf("Can't open display display !!!\n");
		exit(1);
	}
	theScreen = DefaultScreen(theDisplay);
	theRootWindow = DefaultRootWindow(theDisplay);
	theVisual = DefaultVisual(theDisplay,theScreen);
	theGC = DefaultGC(theDisplay,theScreen);
	theDefaultColormap = DefaultColormap(theDisplay,theScreen);

	theWindow = XCreateSimpleWindow(theDisplay,theRootWindow,
					50,50,pict_width(dispPict),pict_height(dispPict),1,0,0);
	if (!theWindow)
	{
		printf("Can't create window !!!\n");
		exit(1);
	}

	sprintf(title,"%s,%d%%,%2d%%,%d",name,
				(int)(sx*100),(int)(sy*100),ncols);
	XStoreName(theDisplay,theWindow,title);
	XMapWindow(theDisplay,theWindow);
	XSelectInput(theDisplay,theWindow,
			KeyPressMask|ExposureMask);

	setXColormap(dispPict);

	theImage = XCreateImage (theDisplay, theVisual, 8, ZPixmap, 0,
				(char *)pict_pixels(dispPict),
				pict_width(dispPict),pict_height(dispPict), 8, 0);
	XPutImage(theDisplay, theWindow, theGC, theImage,
				0, 0, 0, 0, pict_width(dispPict),pict_height(dispPict));

	for (;ever;)
	{
		newcurrent = current + show;
		redraw = 0;
		reload = 0;
		XNextEvent (theDisplay, &event);
		switch (event.type)
		{
			case KeyPress:
				switch(key=XLookupKeysym(&event.xkey,0))
				{
					case XK_Q:
					case XK_q:
					case XK_Escape:
						ever = 0;
						break;
#ifdef	HAVE_JPEG
					case XK_j:
					case XK_J:
						writeJPEG(orgPict,name,quality);
						break;
#endif
					case XK_r:
					case XK_R:
						reload = 1;
						break;
					case XK_n:
					case XK_N:
						if (!show) newcurrent = current + 1;
						break;
					case XK_p:
					case XK_P:
						if (!show) newcurrent = current - 1;
						break;
					case XK_g:
					case XK_G:
						mode = GRAYSCALE;
						redraw = 1;
						break;
					case XK_0:
					case XK_c:
					case XK_C:
						mode = OPTIMAL;
						redraw = 1;
						break;
					case XK_1:
						mode = R_CHAN;
						redraw = 1;
						break;
					case XK_2:
						mode = G_CHAN;
						redraw = 1;
						break;
					case XK_3:
						mode = B_CHAN;
						redraw = 1;
						break;
					case XK_4:
						mode = M_ALPHA;
						redraw = 1;
						break;
					case XK_KP_Add:
						sx *= 2; sy *= 2;
						redraw = 1;
						break;
					case XK_KP_Subtract:
						sx /= 2; sy /= 2;
						redraw = 1;
						break;
					case XK_Up:
						ncols *= 2;
						if (ncols>256) ncols = 256;
						redraw = 1;
						break;
					case XK_Down:
						ncols /= 2;
						if (ncols<2) ncols = 2;
						redraw = 1;
						break;
					default:
/*						printf("keysym %d\n",key);*/
						break;
				}
				break;
			case Expose:
				exp_event = (XExposeEvent *)&event;
				XPutImage (theDisplay, theWindow, theGC, theImage,
					exp_event->x, exp_event->y,
					exp_event->x, exp_event->y,
					exp_event->width, exp_event->height);
				break;
			case DestroyNotify:
				ever = 0;
				break;
		}
		if (newcurrent >= argc) newcurrent=first;
		if (newcurrent <first) newcurrent=argc-1;
		if ((newcurrent != current) || reload)
		{
			pict_free(orgPict);
			current = newcurrent;
			orgPict = pict_read_pict(argv[current]);
			if (fixedw >0) sx = fixedw/(double)pict_width(orgPict);
			if (fixedh >0) sy = fixedh/(double)pict_height(orgPict);
			name = (argv[current]==NULL ? "stdin" : argv[current]);
			redraw = 1;
		}
		sleep(pause);
		if (redraw)
		{
			dispPict = adjustPict(orgPict,dispPict,mode,ncols,sx,sy);
			setXColormap(dispPict);
			theImage = XCreateImage (theDisplay, theVisual, 8, ZPixmap, 0,
					(char *)pict_pixels(dispPict),
					pict_width(dispPict),pict_height(dispPict), 8, 0);
			XResizeWindow(theDisplay,theWindow,
					pict_width(dispPict),pict_height(dispPict));
			sprintf(title,"%s,%d%%,%2d%%,%d",name,
				(int)(sx*100),(int)(sy*100),ncols);
			XStoreName(theDisplay,theWindow,title);
			XPutImage(theDisplay, theWindow, theGC, theImage,
					0, 0, 0, 0, pict_width(dispPict),pict_height(dispPict));
		}
	}
}

Pict	adjustPict(Pict org,Pict disp,int mode,int ncol,double sx,double sy)
{
	int	i,size;
	Pict	aux;

	if (disp!=NULL) pict_free(disp);
	aux = (Pict)malloc(sizeof(PictStruct));
	*aux = *org;
	size = pict_width(org)*pict_height(org)*org->n_comp;
	aux->pixels = malloc(size);
	memcpy(aux->pixels,org->pixels,size);

	if (sx!=1 || sy!=1) scalePict(aux,sx,sy);

	disp = pict_24_to_8(aux,mode,5,ncol);

	for (i=0; i<pict_width(disp)*pict_height(disp); i++)
		disp->pixels[i] += 256-ncol;

	pict_free(aux);

	return disp;
}

#if defined(__STDC__) || defined (__stdc__)
void	scalePict(Pict p,double sx,double sy)
#else
void	scalePict(p,sx,sy)
Pict	p;
double	sx;
double	sy;
#endif
{
	int	nw,nh;
	U_char	r00,g00,b00,a00;
	U_char	r01,g01,b01,a01;
	U_char	r10,g10,b10,a10;
	U_char	r11,g11,b11,a11;
	U_char	*buff,*pix;
	double	ki,kj;
	double	ri,rj,ri1,rj1;
	int	oi,oj,di,dj;

	nw = (int)(sx*p->image_W);
	nh = (int)(sy*p->image_H);

	buff = (U_char *)malloc(4*(size_t)nw*nh+10);

	ki = (double)(p->image_W-1.0)/(double)(nw-1.0);
	kj = (double)(p->image_H-1.0)/(double)(nh-1.0);
	pix = buff;
	for (dj=0; dj<nh; dj++)
	{
		oj = (int)(kj*dj);
		rj = kj*dj - oj;
		rj1 = 1.0 - rj;
		for (di=0; di<nw; di++)
		{
			oi = (int)(ki*di);
			ri = ki*di - oi;
			ri1 = 1.0 - ri;
			pict_pixel_rgba(p,oj  ,oi  ,&r00,&g00,&b00,&a00);
			pict_pixel_rgba(p,oj+1,oi  ,&r10,&g10,&b10,&a10);
			pict_pixel_rgba(p,oj  ,oi+1,&r01,&g01,&b01,&a01);
			pict_pixel_rgba(p,oj+1,oi+1,&r11,&g11,&b11,&a11);
			
			if(p->clr_mode != M_MONO)
			{
			*pix++ = ri1*rj1*r00+ri*rj1*r01+ri1*rj*r10+ri*rj*r11;
			*pix++ = ri1*rj1*g00+ri*rj1*g01+ri1*rj*g10+ri*rj*g11;
			*pix++ = ri1*rj1*b00+ri*rj1*b01+ri1*rj*b10+ri*rj*b11;
			*pix++ = ri1*rj1*a00+ri*rj1*a01+ri1*rj*a10+ri*rj*a11;
			}
			else
			{
			*pix++ =
			255 - (ri1*rj1*r00+ri*rj1*r01+ri1*rj*r10+ri*rj*r11);
			}
		}
	}

	free(p->pixels);
	p->pixels = buff;

	p->image_W = nw;
	p->image_H = nh;
	if (p->clr_mode != M_MONO)
	{
		p->clr_mode = M_RGBA32;
		p->row_bytes = 4*nw;
		p->ctr_size = (p->row_bytes <= 250 ? BYTE : WORD);
		p->n_comp = 4;
	}
}

int	setXColormap(Pict p)
{
	int			i,unusedcols;
	Colormap	myColormap;
	XColor		colval,stdcol[256];
	C_MAP		map;

	myColormap = XCreateColormap(theDisplay,theWindow,theVisual,AllocAll);

	unusedcols = 256-ncols;
	colval.flags = DoRed | DoGreen | DoBlue;
	for (i=0; i<unusedcols; i++) stdcol[i].pixel = i;
	XQueryColors(theDisplay,theDefaultColormap,stdcol,unusedcols);
	for (i=0; i<unusedcols; i++)
	{
		colval.pixel = stdcol[i].pixel;
		colval.red   = stdcol[i].red;
		colval.green = stdcol[i].green;
		colval.blue  = stdcol[i].blue;
		XStoreColor(theDisplay, myColormap, &colval);
	}

	pict_get_map(p,map);
	colval.flags = DoRed | DoGreen | DoBlue;
	for (i=0; i<ncols; i++)
	{
		colval.pixel = unusedcols+i;
		colval.red   = 256*map[i][0];
		colval.green = 256*map[i][1];
		colval.blue  = 256*map[i][2];
		XStoreColor(theDisplay, myColormap, &colval);
	}

	XSetWindowColormap (theDisplay, theWindow, myColormap);
	XInstallColormap (theDisplay, myColormap);
}

#ifdef	HAVE_JPEG
void	writeJPEG(Pict p,char *name,int quality)
{
	char	jname[MAXPATHLEN],*dot;
	struct jpeg_compress_struct	cinfo;
	struct jpeg_error_mgr		jerr;
	FILE	*f;
	int		i,j;
	JSAMPROW	data,aux; /* como un char *; */

	data = (JSAMPROW)malloc(pict_width(p)*3);

	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_compress(&cinfo);

	strcpy(jname,name);
	dot = strrchr(jname,'.');
	if ( (dot!=NULL) && (!strcmp(dot,".pict")) )
		strcpy(dot,".jpg");
	else
		strcat(jname,".jpg");
	f = fopen(jname,"wb");
	jpeg_stdio_dest(&cinfo,f);

	cinfo.image_width = pict_width(p);
	cinfo.image_height = pict_height(p);
	cinfo.input_components = 3;
	cinfo.in_color_space = JCS_RGB;
	jpeg_set_defaults(&cinfo);

	jpeg_set_quality(&cinfo,quality,1);

	jpeg_start_compress(&cinfo, TRUE);

	for (i=0; i<pict_height(p); i++)
	{
		for (j=0,aux=data; j<pict_width(p); j++,aux+=3)
			pict_pixel_rgba(p,i,j,aux,aux+1,aux+2,NULL);
		jpeg_write_scanlines(&cinfo,&data,1);
	}

	jpeg_finish_compress(&cinfo);
	jpeg_destroy_compress(&cinfo);

	close(f);

	free(data);
}
#endif