#include "videoshow.h"
#include "display.h"

#define MODE_PLAY 1
#define MODE_PAUSE 2
#define MODE_STEP 3
#define MODE_NEXT 4

#define STR(foo) #foo
#define XSTR(foo) STR(foo)

static int SDLDepth = 16;

static int MouseX = 0;
static int MouseY = 0;

static int ControlPressed = 0;
static int VideoStack = 1;
static int WriteFrame = 0;

static int WriteAllFrames = 0;
static char *WriteDestination = NULL;

static double TimeCorrection = 0;
static int UseImageMagick = 0;

int Mode = MODE_PLAY;
int VideoCut = 0;
int Offset = 0;
double UMask = 0.0;

static int Requested_FPS = 0;

extern int DetectBrokenFrames;

int SkipCount = 1;	// -skip commandline arg, draw all frames by default
int DrawFrame = 1;
int SeekColour = 0;

char * GainComp = NULL;

unsigned long FrameOffsets[10000];
int FrameCount = 0;
static int Current_File = 0;
static int Playing_File = 0;

static int Aperture=0;
static int ApertureX = 0;
static int ApertureY = 0;
static int ApertureW = 20;
static int ApertureH = 20;

static void MouseMotion(int x, int y);
static int FrameChannel(char *fname);
static void Sharpen(unsigned int *buffer, int width, int height);
static void Smooth(unsigned int *buffer, int width, int height);
static int Convert_8_16_bpp(struct Image *img);
int isBrokenFrame(struct Image *img);

static int add_to_playlist(char *);
static int play_all(void);

#define PROPERTIES_MAX 7
#define PROPERTIES_DEF 0

int PropList[PROPERTIES_MAX + 1];
static int cur_channel = PROPERTIES_DEF;

static void
MouseMotion(int x, int y)
	{
	MouseX = x;
	MouseY = y;
	}

static void
VKeyPress(int key, int mod)
	{
	double g;

	switch(key) {
	   case SDLK_ESCAPE:
	   case SDLK_q:
		SDL_Quit();
		exit(1);
		break;
	   case SDLK_a:
		Aperture = 1;
		ApertureX = MouseX - ApertureW/2;
		ApertureY = MouseY - ApertureH/2;
		break;
	   case SDLK_h:
		if (Aperture && ApertureX>1) ApertureX--;
		break;
	   case SDLK_l:
		if (Aperture) ApertureX++;
		break;
	   case SDLK_j:
		if (Aperture) ApertureY++;
		break;
	   case SDLK_k:
		if (Aperture) ApertureY--;
		break;
	   case SDLK_LCTRL:
		ControlPressed |= 1;
		break;
	   case SDLK_RCTRL:
		ControlPressed |= 2;
		break;
	   case SDLK_n:
		Mode = MODE_NEXT;
		break;
	   case SDLK_SPACE:
		if (Mode == MODE_PLAY) Mode = MODE_PAUSE;
		else Mode = MODE_PLAY;
		break;
	   case SDLK_RIGHT:
		Mode = MODE_STEP;
		break;
	   case SDLK_MINUS:
		Current_File -= 1000;
		if (Current_File<0) Current_File = 0;
		break;
	   case SDLK_EQUALS:
		Current_File += 1000;
		break;
	   case SDLK_LEFT:
		if (FrameCount>1) {
		   Mode = MODE_STEP;
		   FrameCount-=2;
		   }

	  	if (Playing_File && Current_File>1) {
		   Mode = MODE_STEP;
		   Current_File -= 2;
		   }

		break;
	   case SDLK_1:

		if (ControlPressed) SkipCount = 1;
		break;

	   case SDLK_2:

		if (ControlPressed) {
		   SkipCount = 2;
		   break;
		   }

		DrawFrame=0;
		SeekColour = 2;
		break;
	   case SDLK_3:

		if (ControlPressed) {
		   SkipCount = 3;
		   break;
		   }

		DrawFrame=0;
		SeekColour = 3;
		break;
	   case SDLK_4:
		DrawFrame=0;
		SeekColour = 4;
		break;
	   case SDLK_5:
		DrawFrame=0;
		SeekColour = 5;
		break;
	   case SDLK_6:
		DrawFrame=0;
		SeekColour = 6;
		break;
	   case SDLK_w:
		WriteFrame = 1;
		break;
	   case SDLK_r:
		SetProperty_gain(PropList[cur_channel], 1.0);
		break;
	   case SDLK_RIGHTBRACKET:
		AdjustProperty_gain(PropList[cur_channel], 0.1);
		break;
	   case SDLK_LEFTBRACKET:
		g = AdjustProperty_gain(PropList[cur_channel], -0.1);
		if (g<0.5) SetProperty_gain(PropList[cur_channel], 0.5);
		break;

	   case SDLK_m:
		Print("Mouse coords (%d,%d)\n",MouseX,MouseY);
		break;
	   }
	}

static void
VKeyRelease(int key, int mod)
	{

	switch(key) {
	   case SDLK_LCTRL:
		ControlPressed &= 0xffe;
		break;
	   case SDLK_RCTRL:
		ControlPressed &= 0xffd;
		break;
	   }
	}

int FCount=0;

static int
FrameChannel(char *fname)
	{
	char *ptr = fname + strlen(fname)-1;
	int ch = atoi(ptr-4);

	if (ch<0 || ch>6) ch = 0;

	return ch;
	}

static void
CutOutSubregion(struct Image *img)
	{
        if (SR_X1 >= img->width || SR_X2 >= img->width || SR_Y1 >= img->height ||
                SR_Y2 >= img->height) {
                Print("Error: Subregion (%d,%d,%d,%d) outside image dimensions of %d x %d\n",
                        SR_X1,SR_Y1,SR_X2,SR_Y2,img->width, img->height);
                exit(1);
                }

        // Now set the cutout region and recreate the image
        img->cutout.x1 = SR_X1;
        img->cutout.x2 = SR_X2;
        img->cutout.y1 = SR_Y1;
        img->cutout.y2 = SR_Y2;

        CutX = newWidth  = SR_X2-SR_X1+1;
        CutY = newHeight = SR_Y2-SR_Y1+1;

	//Print("CutOut: (%d,%d) - (%d,%d)\n",SR_X1,SR_Y1,SR_X2,SR_Y2);
        CutOut(img);

	img->dst_width = newWidth;
	img->dst_height = newHeight;
	}

// Draw a box around the outside of the aperture region
static void
ShowAperture(struct Image *img)
	{
	int w = img->width;
	int h = img->height;
	int x,y,o;
	int tl = w * (ApertureY-1) + (ApertureX-1);
	int tr = tl + ApertureW+1;
	int bl = tl + w * (ApertureH+1);
	unsigned char *ptr = img->data;
	unsigned short *uptr = (unsigned short *) img->data;

	if (ApertureX<1 || ApertureY<1 || (ApertureY+ApertureH+1)>=h || (ApertureX+ApertureW+1)>=w) 
	   return;

	switch(img->depth) {
	   case 8:
		for(o=tl,x=ApertureW+2; x>0; --x,++o) ptr[o] = 0xff;
		for(o=bl,x=ApertureW+2; x>0; --x,++o) ptr[o] = 0xff;

		for(o=tl,y=ApertureH+2; y>0; --y,o+=w) ptr[o] = 0xff;
		for(o=tr,y=ApertureH+2; y>0; --y,o+=w) ptr[o] = 0xff;
	   	break;

	   case 16:
		for(o=tl,x=ApertureW+2; x>0; --x,++o) uptr[o] = 0xffff;
		for(o=bl,x=ApertureW+2; x>0; --x,++o) uptr[o] = 0xffff;

		for(o=tl,y=ApertureH+2; y>0; --y,o+=w) uptr[o] = 0xffff;
		for(o=tr,y=ApertureH+2; y>0; --y,o+=w) uptr[o] = 0xffff;
	   	break;
		
	   default:
		Print("ShowAperture: Depth %d not supported\n",img->depth);
		exit(1);
		break;
	   }
	}

static double
AnalyseAperture(struct Image *img)
	{
	int w = img->width;
	int h = img->height;
	int x,y,o,oo;
	int tl = w * ApertureY + ApertureX;
	unsigned char *ptr = img->data;
	unsigned short *uptr = (unsigned short *) img->data;
	double total=0;

	if (ApertureX<1 || ApertureY<1 || (ApertureY+ApertureH+1)>=h || (ApertureX+ApertureW+1)>=w) 
	   return 0;

	switch(img->depth) {
	   case 8:
		for(o=tl,y=ApertureH; y>0; --y, o+=w)
		   for(oo=o,x=ApertureW; x>0; ++oo, --x)
			total += ptr[oo];

	   	break;

	   case 16:
		for(o=tl,y=ApertureH; y>0; --y, o+=w)
		   for(oo=o,x=ApertureW; x>0; ++oo, --x)
			total += uptr[oo];

	   	break;
		
	   default:
		Print("ShowAperture: Depth %d not supported\n",img->depth);
		exit(1);
	   }

	return total;
	}

static void
UpdateApertureCentre(struct Image *img)
	{
	int w = img->width;
	int h = img->height;
	int x,y,o,oo;
	int tl = w * ApertureY + ApertureX;
	int cx,cy,npix,thresh,max;
	unsigned char *ptr = img->data;
	unsigned short *uptr = (unsigned short *) img->data;
	double val,total;

	cx=cy=npix=0;

	switch(img->depth) {
	   case 8:
		// Find brightest pixel
		max=0;
		for(o=tl,y=0; y<ApertureH; ++y, o+=w)
		   for(oo=o,x=0; x<ApertureW; ++oo, ++x)
			if (ptr[oo]>max) max=ptr[oo];

		max *= 0.80;

		for(o=tl,y=0; y<ApertureH; ++y, o+=w)
		   for(oo=o,x=0; x<ApertureW; ++oo, ++x) {
			if (ptr[oo] > max) {
			   cx += x;
			   cy += y;
			   npix++;
			   }
			}

	   	break;

	   case 16:
		// Find brightest pixel
		max=0;
		for(o=tl,y=0; y<ApertureH; ++y, o+=w)
		   for(oo=o,x=0; x<ApertureW; ++oo, ++x)
			if (ptr[oo]>max) max=ptr[oo];

		max *= 0.80;

		for(o=tl,y=0; y<ApertureH; ++y, o+=w)
		   for(oo=o,x=0; x<ApertureW; ++oo, ++x) {
			if (uptr[oo] > max) {
			   cx += x;
			   cy += y;
			   npix++;
			   }
			}

	   	break;
		
	   default:
		Print("ShowAperture: Depth %d not supported\n",img->depth);
		exit(1);
	   }

	if (npix) {
	   int nx = cx/npix;
	   int ny = cy/npix;

	   if (nx<ApertureW/2) ApertureX--;
	   if (nx>ApertureW/2) ApertureX++;
	   if (ny<ApertureH/2) ApertureY--;
	   if (ny>ApertureH/2) ApertureY++;

	   //ApertureX = ApertureX + (cx/npix) - ApertureW/2;
	   //ApertureY = ApertureY + (cy/npix) - ApertureH/2;
	   }

Print("Aperture now %d,%d\n",ApertureX,ApertureY);

	}

void
display_image(struct Image *img)
	{
	static struct Image *d_img = NULL;
	int i,npixels = img->width * img->height;
	int str_y,str_x;
	SDL_Event ev;
	static int have_window = -1;
	static int Width=-1,Height=-1;
	static unsigned long tm;
	static int frame_count=0;
	struct tm *t;
	unsigned char *data,*ddata;
	unsigned short *u_data,*u_ddata;
	static char str[128] = { 0 };
	char tmstr[128];
	char img_name[64];
        double tm_sec = img->tm_sec + TimeCorrection;
        unsigned long long tm_tsc = img->tm_tsc;
	unsigned int hr = tm_sec/3600;
	unsigned int min = (tm_sec - hr*3600)/60;
	double secs = tm_sec - hr*3600 - min*60;
	static int ApCount=0;

	SaveGlobals();

	if (img->src_fname) {
	   // remove directory component
	   char *ptr = img->src_fname + strlen(img->src_fname)-1;
	   while(ptr > img->src_fname && (*(ptr-1) != '/' && *(ptr-1) != '\\')) --ptr;
	   strcpy(img_name,ptr);
	   }
	else
	   strcpy(img_name,"unknown");

	if (DetectBrokenFrames && isBrokenFrame(img))
	   return;

	if (FCount==0) tm = time(0);

	if (AMean) AlphaTrimmedMean(img,1,2);

	//if (img->depth == 8) {
	//   Convert_8_16_bpp(img);
	//   }

	if (! DrawFrame) {
	   int c = FrameChannel(img->src_fname);
	   if (c == SeekColour) DrawFrame=1;
	   }

	if (DrawFrame) {

	   if (0 && GainComp) 
		   // moved to fileio.c
		   ApplyGainCompensation(img);

	   if (VideoCut) {
             // If we have specified a -subregion then we now cut this out and use it
             // instead of the whole centered image
             if (EnableSubRegion) {
		CutOutSubregion(img);
		}
	     else {
	      	CalculateCutout(img);
	      	CutOut(img);
	      	}
	     }

	   if (d_img == NULL) {
		   //Print("Converting 16 -> 16\n");
		   d_img = ConvertImage(img,IMG_FIT,img->depth);
		   }

	   d_img->src_fname = strdup(img_name);
	   d_img->dst_width = img->dst_width;
	   d_img->dst_height = img->dst_height;

	   if (WriteDestination != NULL) {
		char str[1024];
		sprintf(str,"%s/%s",WriteDestination,img_name);
	   	d_img->dst_fname = strdup(str);
		}
	   else
	   	d_img->dst_fname = strdup(img_name);

	   data = img->data;
	   ddata = d_img->data;

	   u_data = (unsigned short *) img->data;
	   u_ddata = (unsigned short *) d_img->data;

	   if (frame_count == 0) {
		if (img->depth == 8) memcpy(ddata,data,npixels); 
		if (img->depth == 16) memcpy(u_ddata,u_data,npixels * 2); 
		}
	   else {
		if (img->depth == 8) {
		   for(i=0; i<npixels; ++i) {
		 	unsigned int v = ddata[i];
		 	v += data[i];
		 	v /= 2;
		 	ddata[i] = v;
		 	}
		   }
		else if (img->depth == 16) {
		   for(i=0; i<npixels; ++i) {
		 	unsigned int v = u_ddata[i];
		 	v += u_data[i];
		 	v /= 2;
		 	u_ddata[i] = v;
		 	}
		   }
		}

	   frame_count++;
	   if (frame_count < VideoStack) return;

	   if (Width != d_img->width || Height != d_img->height) {
	      if (have_window) SDL_Quit();
	      SDLDepth = 16;
	      have_window = InitDisplay("VideoShow",d_img->width,d_img->height,SDLDepth);
	      if (! have_window) {
		   SDLDepth = 32;
		   fprintf(stderr,"16bpp failed, trying 32...\n");
	           have_window = InitDisplay("VideoShow",d_img->width,d_img->height,SDLDepth);
		   if (! have_window) FatalError("Cannot start SDL");
		   }

	      Width = d_img->width; Height = d_img->height;
	      }

	   sprintf(str,"%-20.20s ", img_name);

	   if (tm_sec) {
	        sprintf(tmstr,"%02d:%02d:%02.6lf",hr,min,secs);
		strcat(str," ");
		strcat(str,tmstr);
		}
	   else tmstr[0]=0;

	   if (tm_tsc) {
#ifdef MSWIN32
	      sprintf(str+strlen(str)," / %I64u",tm_tsc);
#else
	      sprintf(str+strlen(str)," / %llu",tm_tsc);
#endif
	      }

	   // Where do we display it? 
	   str_x = d_img->cutout.x1 + 10;
	   str_y = d_img->cutout.y2 - 20;

	   DrawString(d_img, str_y, str_x, str, DRAW_BLACK_PIXELS);

	   if (Aperture) {
		double val = AnalyseAperture(d_img);

		fprintf(stderr,"'%s','%s','%f'\n",img_name,tmstr,val);
		ShowAperture(d_img);

		++ApCount;
		if (ApCount == 10) {
		   //UpdateApertureCentre(d_img);
		   ApCount=0;
		   }
		}

	   // Show mouse coords
	   str_x = d_img->cutout.x2 - 70;
	   str_y = d_img->cutout.y1 + 2;
	   sprintf(str,"%03d,%03d",MouseX,MouseY);
	   DrawString(d_img,str_y,str_x,str, DRAW_BLACK_PIXELS);

	   DisplayImage(d_img, PropList[cur_channel], 0, 0);
	   frame_count=0;

	   if (Requested_FPS > 0) 
	      Usleep(1000000 / Requested_FPS);

	   }

	if (Mode == MODE_STEP) Mode = MODE_PAUSE;

	while(1) {
	   while(SDL_PollEvent(&ev)) {
	      switch(ev.type) {
		case SDL_KEYDOWN: VKeyPress(ev.key.keysym.sym, ev.key.keysym.mod);
				  break;
		case SDL_KEYUP: VKeyRelease(ev.key.keysym.sym, ev.key.keysym.mod);
				  break;
		case SDL_MOUSEMOTION:
			MouseMotion(ev.motion.x,ev.motion.y);
			break;
		}
	     }
	   if (WriteFrame == 1) {
		//printf("Write [%s]\n",d_img->dst_fname);
		WriteImage(d_img);
		WriteFrame=0;
		}

	   if (Mode == MODE_PLAY || Mode == MODE_STEP || Mode == MODE_NEXT) break;
	   Usleep(10000);
	   }

	// -writeallframes commandline option
	if (WriteAllFrames == 1) {
	   //printf("Write [%s]\n",d_img->dst_fname);
	   WriteImage(d_img);
	   }

	++FCount;
	if (time(0) - tm > 2) {

	   FCount /= 2;
	   printf("%-15.15s fps: %5.5d ",img_name,FCount);

	   printf("%02d:%02d:%02.6lf", hr,min,secs);

#ifdef MSWIN32
	   printf(" / %I64u\r", tm_tsc);
#else
	   printf(" / %llu\r", tm_tsc);
#endif
	   fflush(stdout);
	   FCount=0;
	   }

	return;
	}

static int
comp(const void *ptr1, const void *ptr2)
        {
        char **s1 = (char **)ptr1;
        char **s2 = (char **)ptr2;
        char *p1 = *s1;
        char *p2 = *s2;
        int i1,i2;

        // seek to end of string
        p1 += strlen(p1);
        p2 += strlen(p2);

        if (strcasecmp(p1-4,".fta")==0 && strcasecmp(p2-4,".fta")==0) {
           // comparison on these archive files is purely string
           return strcasecmp(*s1,*s2);
           }

        // seek back until we find the last path entry
        while(p1 != *s1 && *(p1-1) != '/' && *(p1-1) != '\\') --p1;
        while(p2 != *s2 && *(p2-1) != '/' && *(p2-1) != '\\') --p2;

        // Go forward until we find a digit
        while(*p1 && ! isdigit(*p1)) ++p1;
        while(*p2 && ! isdigit(*p2)) ++p2;

        // Hopefully we are now on a substring that can be used for comparison
        i1 = atoi(p1);
        i2 = atoi(p2);

        if (i1<i2) return -1;
        if (i1>i2) return 1;
        return 0;
        }

static char *filelist[100000];
static int FileCount=0;

void
playdir(char *dir)
	{
	struct dirent *e;
	int i;
	DIR *d = opendir(dir);
	char fname[256];

	Mode = MODE_PLAY;
	FileCount = 0;

	while(e = readdir(d)) {
	   if (strlen(e->d_name)>4 && e->d_name[0] != '.') {
		sprintf(fname,"%s/%s",dir,e->d_name);
		if (! access(fname,R_OK|F_OK) && isSupportedFile(fname)) {
		   filelist[FileCount++] = strdup(fname);
		   if ((FileCount%1000) == 0) {
			printf("Loaded %d files...\n",FileCount);
			fflush(stdout);
			}
		   }
		}
	   }

	closedir(d);

	printf("Sorting %d files...\n",FileCount);
	fflush(stdout);

	Current_File =0;

	qsort(filelist,FileCount,sizeof(char *),comp);
	while(Current_File < FileCount) {
	   if (Current_File<0) Current_File=0;
	   Playing_File = 1;
	   if (Mode != MODE_NEXT) playfile(filelist[Current_File++]);
	   Playing_File = 0;
	   }

	FileCount=0;
	}

void
playfile(char *fname)
	{
	struct Image *img;
	int len = strlen(fname);
	static int count=0;

	if (! strcmp(fname,"-")) {
	   while (img = LoadImage(fname,NULL)) {
		display_image(img);
	      	DestroyImage(img);
	 	}
	   return;
	   }

	if (! strcmp(fname+len-4,".fta") || !strcmp(fname+len-4,".ftz")) {
	   char ftmp[1024];

	   strcpy(ftmp,fname);
	   playarchive(ftmp);

	   while (ChainArchives) {
		char next_fname[1024];
		int channel=0;

		while(1) {
		   channel = ArchiveNextFilename(ftmp,next_fname);
		   if (! channel) break;

		   if (DrawFrame==0 && SeekColour) {
			if (channel==SeekColour) {
				DrawFrame=1; SeekColour=0;
				break;
				}
			}
		   else break;
		   strcpy(ftmp,next_fname);
		   }

		if (channel) {
		   printf("chained -> [%s]\n",next_fname);
		   strcpy(ftmp,next_fname);
		   playarchive(ftmp);
		   }
		else break;
		}
	   }
	else {
	   //printf("%-5d\r",count); count++; fflush(stdout);
	   img = LoadImage(fname,NULL);
	   display_image(img);
	   DestroyImage(img);
	   }
	}

void
playarchive(char *arch)
	{
	struct Image *img;
	extern FILE *fopen64();
	FILE *in = OpenArchive(arch);
	int count,err,flags=0;

	if (! in) {
	   FatalError("Cannot open archive");
	   }

	FrameCount=0;
	FrameOffsets[0] = 0;

	if (!strcmp(arch+strlen(arch)-4,".ftz")) flags = ARCHIVE_COMPRESSED;

	while(1) {
	   int skip=0;
	   int fd = fileno(in);
	   if (lseek(fd,FrameOffsets[FrameCount],SEEK_SET) < 0) perror("");

	   //Print("\nAt offset %ld\n",ftell(in));

	   if (Mode == MODE_NEXT) break;

	   // Do we want to draw this frame or skip it?
	   if (FrameCount % SkipCount) skip=1; else skip=0;

	   if (DrawFrame && ! skip)
	      img = ArchiveLoadNextImage(in,&err,flags|ARCHIVE_LOAD);
	   else
	      img = ArchiveLoadNextImage(in,&err,flags|ARCHIVE_SKIP);
		
	   if (err==0) break; // end of archive
	   if (err<0) {
		printf("Error %d from ArchiveLoadNextImage\n",err);
		FatalError("Error while reading archive");
		}
	   //Print("%-5d (%-5d)\r",FrameCount,FrameOffsets[FrameCount]); fflush(stdout);
	   FrameCount++;
	   FrameOffsets[FrameCount] = lseek(fd,0L,SEEK_CUR);

	   if (! skip && DrawFrame) {
	      	if (PopFilter) pop_filter(img);
	      	display_image(img);
		}

	   DestroyImage(img);
	   if (SeekColour && DrawFrame==0) break;
	   }
	}
	   
	
#define SHARPEN_DIV 6
void
Sharpen(unsigned int *buffer, int width, int height)
	{
	int x,y,o;
	static unsigned int *bptr = NULL;
	static int bufsize = 0;
	int npixels = width * height;

	if (npixels != bufsize) {
	   if (bptr != NULL) free(bptr);
	   bptr = Malloc(npixels * 4);
	   bufsize = npixels;
	   }

	for(o=y=2; y<height-2; ++y)
	   for(x=2; x<width-2; ++x,++o) {
		int v = buffer[o] * 3;
		v -= buffer[o-2]/SHARPEN_DIV;
		v -= buffer[o+2]/SHARPEN_DIV;
		v -= buffer[o-width-width-2]/SHARPEN_DIV;
		v -= buffer[o-width-width]/SHARPEN_DIV;
		v -= buffer[o-width-width+2]/SHARPEN_DIV;
		v -= buffer[o+width-width-2]/SHARPEN_DIV;
		v -= buffer[o+width+width]/SHARPEN_DIV;
		v -= buffer[o+width+width+2]/SHARPEN_DIV;
		//bptr[o] = (unsigned int)((double)v/2.5);
		if (v>0) bptr[o] = v; else bptr[o]=0;
		}

	memcpy(buffer,bptr,npixels*4);
	}

void
UnsharpMask(unsigned int *buffer, int width, int height, double m)
	{
	static int Npixels = -1;
	static unsigned int *bptr = NULL;
	int npixels = width * height;

	if (npixels != Npixels) {
	   if (bptr != NULL) free(bptr);
	   bptr = Malloc(npixels * 4);
	   Npixels = npixels;
	   }
	
	memcpy(bptr, buffer, npixels*4);
	Smooth(bptr, width, height);
	SubtractScale(buffer,bptr,npixels,m);
	Smooth(buffer,width, height);
	}

void
Smooth(unsigned int *buffer, int width, int height)
	{
	int x,y,o;
	static unsigned int *bptr = NULL;
	static int bufsize = 0;
	int npixels = width * height;

	if (npixels != bufsize) {
	   if (bptr != NULL) free(bptr);
	   bptr = Malloc(npixels * 4);
	   bufsize = npixels;
	   }

	for(o=y=0; y<height; ++y)
	   for(x=0; x<width; ++x,++o) {
		int v = buffer[o];
		v += buffer[o-1];
		v += buffer[o+1];
		v += buffer[o-width-1];
		v += buffer[o-width];
		v += buffer[o-width+1];
		v += buffer[o+width-1];
		v += buffer[o+width];
		v += buffer[o+width+1];
		bptr[o] = v/9;
		}

	memcpy(buffer,bptr,npixels*4);
	}

// buf1 -= buf2 * m
void
SubtractScale(unsigned int *buf1, unsigned int *buf2, int npixels, double m)
	{
	while(npixels-- > 0) {
	   int v = *buf1; v -= *buf2 * m;
	   if (v<0) v=0;
	   *buf1 = v;
	   buf1++; buf2++;
	   }
	}

static int
Convert_8_16_bpp(struct Image *img)
        {
        int depth = img->depth;
        int width = img->width;
        int height = img->height;

        if (depth == 8) {
           register unsigned int X;
           int o = width * height;
           unsigned short *buffer = Malloc(o * 2);
           unsigned char *ptr = (unsigned char *)img->data;

           while(o-- > 0) {
                X = ptr[o]; X<<=8; buffer[o]=X;
                }

           free(img->data);
           img->data = (unsigned char *)buffer;

           img->depth = 16;

           if (img->type == IMG_FIT) {
                struct fits_info *fi = img->info;
                //fi->bzero = 32768;
                fi->bzero = 0;
                }
           }

        return 1;
        }

#ifdef MSWIN32
#include <windows.h>

extern int main(int argc, char* argv[]);

int WINAPI WinMain(HINSTANCE instance, HINSTANCE prev_instance, char* command_line, int show_command)
{
    main(__argc, __argv);
}
#endif
 

char **playlist;
int playlist_size=0;
int playcount=0;

static int
add_to_playlist(char *name)
	{
	if (playlist == NULL || playlist_size==0) {
	   playlist_size=1000;
	   playlist = (char **) Malloc(sizeof(char *) * playlist_size);
	   }

	if (playcount == playlist_size) {
	   playlist_size += 1000;
	   playlist = (char **) realloc(playlist, sizeof(char *) * playlist_size);
	   }

	playlist[playcount++] = name;
	return 0;
	}

static int
play_all()
	{
	int i;

	Current_File=0;
	while(Current_File < playcount) {
	   char *name = playlist[Current_File++];

	   if (!strcmp(name,"-")) {
		playfile("-");
		continue;
		}

	   if (isDirectory(name)) {
		printf("playing dir %s\n",name);
	      	playdir(name);
		}
	   else if (isFile(name)) {
		//printf("playing file %s\n",name);
		Playing_File = 1;
	      	playfile(name);
		Playing_File = 0;
		}
	   }

	return(0);
	}

int
main(int argc, char **argv)
	{
	struct Image *img;
	int i;

	InitProperties();

	for(i=0; i<= PROPERTIES_MAX; ++i) {
	   int ch = CreateProperty(0,1.0,1.0);  // brightness offset, gain, gamma
	   PropList[i] = ch;
	   }

	cur_channel = PROPERTIES_DEF;

	Quiet=1;
	InputHistoStretch = 1;

	// Default values
	AutoBlack = 1;
	CutX = CutY = newWidth = newHeight = 480;
	DoCutout=1;
	VideoCut = 1;
	Requested_FPS = 70;

	for(i=1; i<argc; ++i) {
	   char *arg = argv[i];

	   if (!strcmp(argv[i],"-version")) {
		printf("videoshow %s\n",XSTR(VERSION));
		exit(0);
	   	}

	   if (!strncmp(argv[i],"-istretch",9)) {

		if (!strcmp(argv[i]+9,"=off"))
		   InputHistoStretch=0;
		else InputHistoStretch = 1;

		continue;
		}

	   if (!strncmp(argv[i],"-amean",6)) {
		AMean=1;
		continue;
		}

	   if (!strncmp(argv[i],"-threshhold=",12)) {
		ThreshHold = atoi(argv[i]+12);
		continue;
		}

	   if (!strncmp(argv[i],"-input-type=",12)) {
		char *t = argv[i]+12;
		if (! strcasecmp(t,"fit")) InputFileType = IMG_FIT;
		else if (! strcasecmp(t,"bmp")) InputFileType = IMG_BMP;
		else {
		   printf("Unknown option '%s' to -input-type\n",t);
		   exit(1);
		   }
		continue;
		}

	   if (!strcmp(argv[i],"-ob")) {
		EnableHistoWarning = 0;
		continue;
		}

	   if (!strcmp(argv[i],"-forceprocess")) {
		ForceProcess = 1;
		continue;
		}

	   if (!strncmp(argv[i],"-stack=",7)) {
		VideoStack = atoi(argv[i]+7);
		continue;
		}

	   if (!strcmp(argv[i],"-cutout=off")) {
		DoCutout = 0;
		VideoCut = 0;
		continue;
		}

	   if (!strncmp(argv[i],"-cut=",5)) {
		int x,y;

		if (sscanf(argv[i]+5,"%d,%d",&x,&y)==2) {
		   CutX = x;
		   CutY = y;
		   }
		else {
		   int cut = atoi(argv[i]+5);
		   CutX = CutY = newWidth = newHeight = cut;
		   }

		DoCutout=1;
		VideoCut = 1;
		continue;
		}

	   if (!strncmp(argv[i],"-popfilter=",11)) {
		PopFilter = atoi(argv[i]+11);
		if (PopFilter<1) PopFilter = 3;
		continue;
		}

	   if (!strncmp(argv[i],"-fps=",5)) {
		Requested_FPS = atoi(argv[i]+5);
		continue;
		}

	   if (!strncmp(argv[i],"-offset=",6)) {
		Offset = atoi(argv[i]+8);
		continue;
		}

	   if (!strncmp(argv[i],"-gain=",6)) {
		double g = atof(argv[i]+6);
		int ch,j;
		
		for(j=0; j<= PROPERTIES_MAX; ++j) {
		   ch = PropList[j];
		   SetProperty_gain(PropList[j],g);
		   }

		continue;
		}

	   if (!strncmp(argv[i],"-gain",5) && isdigit(argv[i][5])) {
		int t = atoi(argv[i]+5);
		SetProperty_gain(PropList[t],atof(argv[i]+7));
		continue;
		}

	   if (!strncmp(argv[i],"-amean",6)) {
		AMean = 1;
		continue;
		}

	   if (!strncmp(argv[i],"-magick",3)) {
		UseImageMagick=1;
		continue;
		}

	   if (!strncmp(argv[i],"-dbf",3)) {
		DetectBrokenFrames=1;
		DBF_TYPE=DBF_PLANET;
		continue;
		}

	   if (!strcmp(argv[i],"-chain")) {
		ChainArchives=1;
		continue;
		}

	   // -skip=1 means draw every frame (default)
	   // -skip 2 means draw every 2nd frame
	   // -skip=3 means draw every 3rd frame, etc
	   if (!strncmp(argv[i],"-skip=",6)) {
		SkipCount = atof(argv[i]+6);
		continue;
		}

	   if (!strncmp(argv[i],"-umask=",7)) {
		UMask = atof(argv[i]+7);
		continue;
		}

	   if (!strncmp(argv[i],"-gamma=",7)) {
		double g = atof(argv[i]+7);

		SetProperty_gamma(PropList[cur_channel],g);
		continue;
		}

	   if (!strncmp(argv[i],"-gaincomp=",10)) {
		GainComp = strdup(argv[i]+10);
		if (! LoadGainCompRef(GainComp)) {
		   printf("gaincomp %s failed\n",GainComp);
		   exit(1);
		   }
		continue;
		}

	   if (!strcmp(argv[i],"-noautoblack")) {
		AutoBlack = 0;
		continue;
	   	}

	   if (!strcmp(argv[i],"-stdin")) {
		add_to_playlist("-");
		continue;
		}

	   if (!strncmp(argv[i],"-timecorrection=",16)) {
		TimeCorrection = atof(argv[i]+16);
		printf("Adding %f to timestamps\n",TimeCorrection);
		continue;
		}

	   if (!strcmp(argv[i],"-writeallframes")) {
		WriteAllFrames=1;
		continue;
		}

	   if (!strncmp(argv[i],"-subregion=",11)) {
		int x1,y1,x2,y2;
		EnableSubRegion=1;
        	if (sscanf(argv[i]+11,"%d,%d,%d,%d",&x1,&y1,&x2,&y2) == 4) {
        		SR_X1 = x1;
        		SR_X2 = x2;
        		SR_Y1 = y1;
        		SR_Y2 = y2;
			}
		else if (sscanf(argv[i]+11,"%d,%d,%dx%d",&x1,&y1,&x2,&y2) == 4) {
        		SR_X1 = x1;
        		SR_X2 = x1+x2-1;
        		SR_Y1 = y1;
        		SR_Y2 = y1+y2-1;
			}
		else {
           	   printf("-subregion: Invalid format, should be -subregion=x1,y1,x2,y2 or -subregion=x1,y1,WxH\n");
           	   exit(1);
           	   }

		printf("Setting subregion to (%d,%d) - (%d,%d) width=%d, height=%d\n",x1,y1,x2,y2,x2-x1+1,y2-y1+1);

        	DoCutout=1;
		continue;
		}

	   if (!strncmp(argv[i],"-writeto=",9)) {
		WriteDestination = strdup(argv[i]+9);
		printf("Set '%s' as write destination\n",WriteDestination);

		if (! isDirectory(WriteDestination)) {
		   Mkdir(WriteDestination);
		   }

		if (! isDirectory(WriteDestination)) {
		   Print("cannot create destination '%s'\n",WriteDestination);
		   exit(1);
		   }

		continue;
		}
	   if (isDirectory(arg)) {
	      	add_to_playlist(arg);
		}
	   else if (isFile(arg)) {
	      	add_to_playlist(arg);
		}
	   }

	play_all();
	ShutdownDisplay();
	}
