#include "ninox.h"
#include "SDL/SDL.h"
#include <sys/stat.h>

static void Canonise_src_fname(char *src_fname);
static void Set_CurrentFile(char *src_fname);
static int ProcessImage(struct Image *img);

static int LoadLogfile(char *dir, char *name);
static char * GetLogEntry(char *str);

static int AlignSingleFile(char *name);
static int AlignArchive(char *name);

static int AlignBMP(char *fname, char *out_fname);
static int AlignFIT(char *fname, char *out_fname);
int isBrokenFrame(struct Image *img);

static int _ProcessOffset = 0;
static int _Align_Count = 0;	// Count of how many times we've called into Align
				// so we can intelligently set the Outdir

static char OutDir_Last[256];	// Last Outdir

int
Align(char *fname)
	{
	char OutDir_save[256];
	char *ptr = fname + strlen(fname)-1;
	int count=0;

	if (! _Align_Count) OutDir_Last[0]=0;

	if (ImageCount == 0) return 0;

	if (StackCount>0 && StackCount>=StackMax) {
	   Printf("stack count reached, processing stopped\n");
	   return 0;
	   }

	// Are we reading from stdin ?
	if (!strcmp(fname,"-"))
	   return AlignSingleFile(fname);
	
	while(ptr>fname && *ptr != '.') --ptr;

	// Is it an archive FITS or SER file ?
	if (	!strcasecmp(ptr,".fta") || 
		!strcasecmp(ptr,".ftz") ||
		!strcasecmp(ptr,".ser")
		) {
again:
	   strcpy(OutDir_save, OutDir);
	   if (! _Align_Count) {
	      // First time in, we may set the OutDir
	      if (OutDirPrependSrc && ! strcasecmp(ptr,".ser") && !strncmp(OutDir,"./",2)) {
		int ymd,hms,dummy;
		int got_result = 0;
		char *p1 = fname + strlen(fname);

		while(p1 != fname && *(p1-1) != '/' && *(p1-1) != '\\') --p1;

		while(*p1 != '.' && ! isdigit(*p1)) ++p1;

		// Check if this is a winjupos de-rotated video
		if (sscanf(p1,"%4d-%2d-%2d-%4d_%d-%8d_%6d",&dummy,&dummy,&dummy,&dummy,&dummy,&ymd,&hms) == 7)
		   got_result = 1;

		if (!got_result && sscanf(p1,"%8d_%6d_",&ymd,&hms)==2) got_result = 1;

		if (got_result) {
		   sprintf(OutDir,"./%06dUTC",hms);
		   Mkdir(OutDir);
		   strcat(OutDir,OutDir_save+1); 
		   Mkdir(OutDir);
		   }
		}
	      }
	   else {
		// If this is a _G or _B then re-use the OutDir from _R
		if (	!strcasecmp(ptr-2,"_G.ser") || 
			!strcasecmp(ptr-8,"_G-DeRot.ser") ||
			!strcasecmp(ptr-2,"_B.ser") ||
			!strcasecmp(ptr-8,"_B-DeRot.ser"))
		     strcpy(OutDir,OutDir_Last);
		}

	   count += AlignArchive(fname);
	   ++_Align_Count;
	   strcpy(OutDir_Last, OutDir);
	   strcpy(OutDir,OutDir_save);

	   if (ChainArchives && ImageCount>0) {
		char nfname[256];
		int found_next=ArchiveNextFilename(fname,nfname);

	  	if (found_next) {
		   strcpy(fname,nfname);
		   goto again;
		   }
		}

	   return count;
	   }

	// Otherwise it must be a simple file
	--ImageCount;
	return AlignSingleFile(fname);
	}

static void
Canonise_src_fname(char *src_fname)
	{
	int i;

	// Canonise filename
	for(i=0; src_fname[i]; ++i)
	   if (src_fname[i]=='\\') src_fname[i]='/';

	// Get rid of leading "./"
	if (src_fname[0]=='.' && src_fname[1]=='/') {
	   for(i=2; src_fname[i]; ++i) src_fname[i-2]=src_fname[i];
	   src_fname[i-2]=0;
	   }

	}
	
static void
Set_CurrentFile(char *src_fname)
	{
	char *p;

	p = src_fname;
	while(*p) ++p; --p;
	while(p != src_fname && *(p-1) != '/') --p;
	CurrentFile = strdup(p);
	}

char *
GenerateDstFilename(char *src_fname)
	{
	char *dst_fname = strdup(src_fname);
	
	// If -outdir option is given write the files to the named directory
	// instead of overwriting the original files. OutDir is relative to
	// the source file directory
	if (OutDir) {
	   char *strtmp = strdup(src_fname);
	   char *ptr = strtmp + strlen(strtmp) -1;
	   int num;
	   char sfx[10],type[10],*pfx_dir;

	   while(ptr != strtmp && *ptr != '/') --ptr;
	   if (ptr != strtmp) {
		*(ptr++)=0;
		pfx_dir = strtmp;
		}
	   else {
		pfx_dir=".";
		}

	   // Look for filename "dddddd-X.sss" d=digits, sss=suffix, X is
	   // identifier. If found, create subdirectory "X"
	   type[0] = 0;
	   if (SubDirs && sscanf(ptr,"%d-%1s.%s",&num,type,sfx) != 3) type[0]=0;

	   //printf("src_fname=[%s] pfx_dir=[%s] type=[%s] OutDir=[%s]\n",src_fname, pfx_dir,type,OutDir);

	   free(dst_fname);
	   dst_fname = malloc(strlen(pfx_dir) + strlen(OutDir) + 
			strlen(type) + strlen(ptr) + 4);
	   if (! dst_fname) {
		Print("Align: Out of Memory\n");
		exit(1);
		}

	    // If OutDir is absolute, then just use it, else if it is relative
	    // then prepend our directory prefix
	    if (OutDir[0]=='/' || OutDir[1]==':' || !strncmp(OutDir,"./",2)) {
		strcpy(dst_fname,OutDir);
		}
	    else
	    	sprintf(dst_fname,"%s/%s",pfx_dir,OutDir);

	    if (! NoSave) Mkdir(dst_fname);

	     if (type[0]) {
		strcat(dst_fname,"/");
		strcat(dst_fname,type);
		if (! NoSave) Mkdir(dst_fname);
		if (forceWriteEmptyFiles) 
		     WriteEmptyFiles = 1;
		else WriteEmptyFiles=0;
		}

	   strcat(dst_fname,"/");
	   strcat(dst_fname,ptr);
	   free(strtmp);
	   }

	// Files that have ".fits" should be converted to ".fit"
	// This is easy, simply chomp off the last char
	if (!strcasecmp(dst_fname+strlen(dst_fname)-5,".fits"))
	   dst_fname[strlen(dst_fname)-1]=0;

	return dst_fname;
	}

// Return 1 if we should keep going, 0 if we should stop
static int
CheckRunfile()
	{
	static unsigned long last_runfile_check = 0;

	if (RunFile && time(0) - last_runfile_check > 0) {
	   struct stat st;

	   if (access(RunFile,0)) {
		if (!Quiet) Print("RunFile has been removed, exiting\n");
		return(0);
		}

	   while(1) {
	      stat(RunFile,&st);
	      int age = time(0) - st.st_mtime;
	      if (age <= 1) break;
	      if (age>=5) {
		 Print("Runfile is stale, quitting.\n");
	         unlink(RunFile);
	         return(0);
		 }
	      Usleep(100 * 1000);
	      }
	   }

	return (1);
	}

static int 
AlignSingleFile(char *src_fname)
	{
	char *p,*dst_fname = NULL;
	int i,rval=1;
	struct Image *img = NULL;
	static int _process_skip=9999;
	static int last_runfile_check = 0;

	if (! CheckRunfile()) { exit(1); }

	if (ProcessOffset>_ProcessOffset) {++_ProcessOffset; goto end;}
	if (_process_skip < ProcessSkip) {_process_skip++; goto end;}
	_process_skip=0;
   
	if (strcmp(src_fname,"-")) {
	   Canonise_src_fname(src_fname);
	   dst_fname = GenerateDstFilename(src_fname);

	   Set_CurrentFile(src_fname);
	   }

	img = LoadImage(src_fname,dst_fname);
	if (! img) {
	   Print("Error Loading '%s'\n",src_fname);
	   rval=0; goto end;
	   }

	if (! strcmp(src_fname,"-")) {
	   if (OutDir && OutDirPrependSrc) {
		char *end = img->src_fname + strlen(img->src_fname) - 1;
		char *ptr,*nptr;
		int ymd,n,hms;
		char *OutDir_Save = OutDir;

		// wind back past the filename portion
		while(end != img->src_fname && (*end != '/' && *end != '\\')) --end;
		if (end != img->src_fname) --end;

		// Wind back past last path component
		ptr = end;
		while(ptr != img->src_fname && (*ptr != '/' && *ptr != '\\')) --ptr;
		if (ptr != img->src_fname) ++ptr;

		// Seek forward until we find the first digit, useful for some firecapture
		// formats where the name starts with a target name that we want to skip
		nptr = ptr;
		while(*nptr && ! isdigit(*nptr)) ++nptr;

		// Look for the recognised stdin filename source formats:
		// YYYYMMDD_N_HHMMSS_
		if (sscanf(nptr,"%8d_%d_%6d_",&ymd,&n,&hms) == 3 ||
		    sscanf(nptr,"%8d_%6d_",&ymd,&hms) == 2
		    		) {
			// Firecapture format
			char dst_tmp[512];
			sprintf(dst_tmp,"%06dUTC",hms);
			if (! isDirectory(dst_tmp) && ! NoSave) Mkdir(dst_tmp);

			strcat(dst_tmp,"/");
			strcat(dst_tmp,OutDir);

			if (! isDirectory(dst_tmp) && ! NoSave) Mkdir(dst_tmp);

			OutDir = dst_tmp; // used inside GenerateDstFilename
			img->dst_fname = GenerateDstFilename(img->dst_fname);
			OutDir = OutDir_Save;
			}
		else if (sscanf(ptr,"data%d-%d-%d",&n,&n,&n) == 3) {
			// Move up one more directory
			if (ptr != img->src_fname) --ptr;
			while(ptr != img->src_fname && (*(ptr-1) != '/' && *(ptr-1) != '\\')) --ptr;
			if (sscanf(ptr,"%06dUTC",&hms) == 1) {
			   char dst_tmp[512];
			   sprintf(dst_tmp,"%06dUTC",hms);
			   if (! isDirectory(dst_tmp) && ! NoSave) Mkdir(dst_tmp);

			   strcat(dst_tmp,"/");
			   strcat(dst_tmp,OutDir);

			   if (! isDirectory(dst_tmp) && ! NoSave) Mkdir(dst_tmp);

			   OutDir = dst_tmp; // used inside GenerateDstFilename
			   img->dst_fname = GenerateDstFilename(img->dst_fname);
			   OutDir = OutDir_Save;
			   }
		   	}
	   	}

	   Set_CurrentFile(img->src_fname);
	   }

	rval = ProcessImage(img);  // img is destroyed in here

	end:

	if (dst_fname) free(dst_fname);
	return rval;
	}

struct {
	char *key;
	char *value;
	} LogLines[1024];

static int LogCount=0;

static int
LoadLogfile(char *dir, char *n)
	{
	char fname[1024];
	char *p,line[1024];
	int i;
	FILE *z;
	char *name;

	// If the archive name ends in ".ser" then change it to ".txt"
	name = Malloc(strlen(n)+4);
	strcpy(name,n);

	p = name + strlen(name)-4;
	if (! strcasecmp(p,".ser")) strcpy(p,".txt");
	else strcat(name,".txt");

	// Look in the same dir as the archive
	sprintf(fname,"%s/%s",dir,name);
	Print("Look for logfile '%s'\n",fname); fflush(stdout);
	if (isFile(fname)) goto load_file;

	sprintf(fname,"%s/log/%s",dir,name);
	Print("Look for logfile '%s'\n",fname); fflush(stdout);
	if (isFile(fname)) goto load_file;

	sprintf(fname,"%s/logs/%s",dir,name);
	Print("Look for logfile '%s'\n",fname); fflush(stdout);
	if (isFile(fname)) goto load_file;

	free(name);
	return 0;

load_file:

	while(0 && LogCount>1) {
	   // Some bug causes this to crash so don't do it. The leak is tiny
	   --LogCount;
	   if (LogLines[LogCount].value != NULL) free(LogLines[LogCount].value);
	   if (LogLines[LogCount].key != NULL) free(LogLines[LogCount].key);
	   }

	LogCount=0;

	printf("LoadLogfile: Loading %s\n",fname);
	z = fopen(fname,"rb");
	while(1) {
	   line[0]=0;
	   fgets(line,1023,z);
	   if (! line[0]) break;

	   p = line + strlen(line)-1; while (*p=='\r' || *p=='\n') *(p--)=0;

	   // name=value pairs
	   p = strchr(line,'=');
	   if (p) {
		*p = 0;
		LogLines[LogCount].key = strdup(line);
		LogLines[LogCount].value = strdup(p+1);
printf("Loaded entry %d [%s] = [%s]\n",LogCount,LogLines[LogCount].key,LogLines[LogCount].value);
		LogCount++;
		}
	   }

	free(name);
	fclose(z);
	Print("Loaded %d entries from %s\n",LogCount,fname);
	return 1;
	}

static char *
GetLogEntry(char *str)
	{
	int i;

	for(i=0; i< LogCount; ++i)
	   if (!strcasecmp(LogLines[i].key,str)) return LogLines[i].value;
	
	return NULL;
	}

// Open the named archive and load/process each of the files it contains.
// Return the number of files processed.
static int
AlignArchive(char *archive_name)
	{
	char *src_dir,*basename;
	char strtmp[128];
	char *camera,*p,*src_fname;
	int count,i,flags,err=1;
	struct Image *img;
	static int archive_num = 0;   // When overriding filenames in the archive
	static int last_runfile_check = 0;
	static int _want_skip = 0;
	static int CameraLoaded = 0;
        FILE *in = OpenArchive(archive_name);

        if (in == NULL) {
           Print("Cannot open archive '%s'\n",archive_name);
           perror(NULL);
           exit(1);
           }

	flags=0;
	if (! strcmp(archive_name+strlen(archive_name)-4,".ftz")) flags = ARCHIVE_COMPRESSED;
	if (! strcmp(archive_name+strlen(archive_name)-4,".ser")) flags |= ARCHIVE_SER;

	// Re-Init this so we don't keep data from an unrelated source
	if (StreamFilter) init_streamfilter();

	Canonise_src_fname(archive_name);

	// Get the directory where the archive is located
	// If there's no directory then src_dir will be an empty string
	src_dir = strdup(archive_name);
	if (! src_dir) {
	   Print("Malloc Error in strdup\n");
	   exit(1);
	   }

	p = src_dir + strlen(src_dir)-1;
	while(p>src_dir && *p != '/') --p; *p=0;
	basename = p+1;

	// Check for a log file to accompany this archive, either in the same directory as the archive or in 
	// a subdirectory named "log" or "logs"
	LoadLogfile(src_dir,basename);

	camera = GetLogEntry("camera");
	if (LoadedConfig && camera && ! CameraLoaded) {
	   Print("Camera: %s\n",camera);
	   // Attempt to load a section named after the camera
	   LoadConfigFile(NULL,camera,NULL,NULL);
	   CameraLoaded=1;
	   }

	for(i=0; i < _want_skip; ++i) ArchiveLoadNextImage(in,&err,flags|ARCHIVE_SKIP);
	_want_skip=0;

	count=0;
	while(ImageCount>0) {
	   if (StackCount>0 && StackCount>=StackMax) break;

	   if (! CheckRunfile()) {exit(1);}

	   img = ArchiveLoadNextImage(in,&err,flags|ARCHIVE_LOAD);
	   if (err==0) break; // End of archive
	   if (err<0) {
		Print("fatal error while reading archive '%s'\n",archive_name);
		exit(1);
		}

	   ++count;

	   // Check for doInvertImage2 meaning invert every second image
	   if (doInvertImage2 && (count&1)) InvertImage(img);

	   if (ArchiveOverrideFilenames) {
		char *n = Malloc(32);
		char *p1,*p2;

		// find the filename portions before and after the numeric section
		p1 = img->src_fname; while(*p1 && !isdigit(*p1)) ++p1;
		p2 = p1; while(isdigit(*p2)) ++p2;

		if (*p1==0 || *p2==0 || p1==p2) {
		   Print("ArchiveOverrideFilenames: Could not find numeric portion in [%s]\n",img->src_fname);
		   exit(1);
		   }

		Print("ArchiveOverrideFilename: Renaming %s => ",img->src_fname);
		*p1=0;
		sprintf(n,"%s%06d%s",img->src_fname,archive_num,p2);
		Print("%s\n",n);
		++archive_num;
		free(img->src_fname); img->src_fname = n;
		}

	   // Generate a pseudo-filename that reflects what this file would
	   // be called if it were not in the archive, by prepending the
	   // archive directory to the given filename
	   if (src_dir[0] && isDirectory(src_dir)) {
	 	unsigned int ymd, hms;
		char dummy[128];

	      // Handle SER files
	      if (strcasecmp(basename+strlen(basename)-4,".ser")==0 && sscanf(basename,"%8d_%6d_",&ymd,&hms)==2) {
	      	   src_fname = Malloc(strlen(src_dir) + 32);
	      	   sprintf(src_fname,"%s/%06dUTC/%s",src_dir,hms,img->src_fname);
		   }
	      else {
	      	src_fname = Malloc(strlen(src_dir) + strlen(img->src_fname) + 2);
	      	sprintf(src_fname,"%s/%s",src_dir,img->src_fname);
	      	}
	      }
	   else src_fname = strdup(img->src_fname);

	   free(img->src_fname);
	   img->src_fname = src_fname;

	   // now that we have a pseudo filename we can generate
	   // a destination
	   img->dst_fname = GenerateDstFilename(src_fname);

	   p = src_fname + strlen(src_fname)-1;
	   while(p>src_fname && *(p-1) != '/') --p;
	   sprintf(strtmp,"%s:%s",archive_name,p);
	   Set_CurrentFile(strtmp);

	   if (ProcessOffset>_ProcessOffset) {
		++_ProcessOffset;
		DestroyImage(img);
		free(CurrentFile); CurrentFile=NULL;
		continue;
		}

	   // process the image and destroy it
	   ProcessImage(img);
	   if (ImageCount < IMAGECOUNT_MAX) --ImageCount;

	   for(i=0; ProcessOffset==_ProcessOffset && i<ProcessSkip; ++i) {
		ArchiveLoadNextImage(in,&err,flags|ARCHIVE_SKIP);
		if (err<=0) {
			_want_skip = ProcessSkip-i;
			break;
			}
	   	}
	   }

	fclose(in);
	return count;
	}

static int
Hist(struct Image *img)
	{
	int width = img->width;
	int height = img->height;
	int h[65536];
	int npix = width * height;
	unsigned short *data = (unsigned short *)img->data;
	int o;

	
	for(o=0; o<65536; ++o) h[o]=0;
	for(o=0; o<npix; ++o) h[data[o]>>4]++;

	for(o=0; o<65536; ++o)
	   Printf("%d: %d\n",o,h[o]);

	exit(0);
	}

static double
do_sum_flux(struct Image *img)
	{
	int width = img->width;
	int height = img->height;
	int depth = img->depth;
	int i,npix = img->width * img->height;
	unsigned char *ptr = (unsigned char *) img->data;
	unsigned short *uptr = (unsigned short *) img->data;

	double total =0;

	switch(depth) {
	   case 8:
		for(i=2; i<npix; ++i) total += ptr[i];
		break;
	   case 16:
		for(i=2; i<npix; ++i) {
		   unsigned int v = uptr[i]>>8;
		   if (v >= ThreshHold) total += v;
		   }
		break;
	   case 24:
		for(i=9; i<npix; i+=3) {
		   double v = uptr[i]*0.29 + uptr[i+1]*0.58 + uptr[i+2]*0.29;
		   total += v;
		   }
		break;
	   default:
		Print("do_sum_flux: depth %d not supported\n",depth);
		exit(1);
		break;
	   }

	return total;
	}

static int
ProcessImage(struct Image *img)
	{
	static int first=1;
	static int last_runfile_check = 0;
	static int filecount=1;
	int rval=1;

	if (first) {
	   if (UpScale != DownScale) {
	      double scalef = (double)UpScale / (double) DownScale;
              Print("Rescaling by %-2.2f, smoothing is %s\n",scalef,UpScale_Smoothing?"on":"off");
	      }

	   }

	/*
	 * Check if we're counting the flux in this image
	 */
	if (SumFlux) {
	   double flux = do_sum_flux(img);
	   static int FCount=0;
	   FILE *z;

	   if (first) {
		Print("Appending image flux to '%s'\n",FluxFile);
		unlink(FluxFile);
		}

	   z = fopen(FluxFile,"a");
	   if (! z) {
		Print("Cannot open Flux file '%s'\n",FluxFile);
		exit(1);
		}
	   else {
		double fl = img->tm_sec;
		int hr = fl/3600;
		int min = (fl - hr*3600) / 60;
		double secs = fl - hr*3600 - min*60;

		fprintf(z,"%-5d,\"%s\",\"%02d:%02d:%2.6lf\",\"%lf\"\n",
			++FCount,img->src_fname,hr,min,secs,flux);
		fclose(z);
		}
	   }

	/*
	 * De-bayer if needed
	 */
	if (DoDeBayer) {
	   struct Image *new = DeBayer(img,DoDeBayer);
	   DestroyImage(img);
	   img = new;
	   Print("Done Debayer\n");
	   }

	/*
	 * de-protect (remove magic pixels 0 and 1 
	 */
	if (img->depth==8)
	   img->data[0] = img->data[1]=0;
	else if (img->depth == 16) {
	   unsigned short *udata = (unsigned short *)img->data;
	   udata[0]=udata[1]=0;
	   }
	
	/*
	 * Test mode: Subsample input image and write out
	 */
	if (SubsampleMode) {
	   Test_Subsample(img);
	   exit(0);
	   }

	/* levels adjust on input data */
	if (doLevelsAdjust && !LevelsAdjust(img,LevelsMin,LevelsMax)) {
	   Printf("Levels adjust failed\n");
	   exit(1);
	   }

	/* If requested, debayer the image */
	if (Bayer8) FixBayer8(img);

	// Detect Broken Frames
	if (DetectBrokenFrames && isBrokenFrame(img)) {
	   Printf("Frame is broken, skipping\n");
	   rval=1; goto end;
	   }
	   
	// Detect hot pixels
	if (DetectHotPixels) {
	   do_DetectHotPixels(img);
	   }


	// POP filter
	if (PopFilter && PopFilter_When == POPFILTER_BEFORE) {
	   pop_filter(img);
	   }

	if (ColumnCorrection) ApplyColumnCorrection(img);
	if (RowCorrection) ApplyRowCorrection(img);
	if (doAutoRowBalance) AutoRowBalance(img);

	if (DoFFT) {
	   static struct Image *ref = NULL;
	   int dx,dy;

	   if (! ref) ref = LoadImage(FFT_Ref,"FFT REF");

	   if (! ref) {
		Print("DoFFT: Can't load reference image '%s'\n",FFT_Ref);
		exit(0);
		}

	   if (! fft_register_pad(ref,img,FFT_LPF_Radius1,FFT_LPF_Radius2,FFT_LPF_Pow,FFT_Region,&dx,&dy)) {
		Print("fft_register: failed\n");
		exit(0);
		}
	   Print("Done FFT, offset (%d,%d)\n",dx,dy);
	   }

	if (! process(img)) {
	   Print("Image processing failed\n");

	   // Make sure the output file exists even if it is size 0
	   if (access(img->dst_fname,0) && WriteEmptyFiles && NoSave==0) {
		//FILE *out = fopen(img->dst_fname,"wb"); fclose(out);
		rval=1; goto end;
		}
	   rval=1; goto end;
	   }

	if (PopFilter && PopFilter_When == POPFILTER_AFTER) {
	   pop_filter(img);
	   }

        if (UpScale_Smoothing && UpScale>=3 && UpScale_Smoothing_When == SMOOTHING_AFTER) {
             if (UpScale <= 3) smooth_image3x3(img);
	     else smooth_image5x5(img);
	     }

	if (FFT_Filter) fft_filter(img,FFT_LPF_Radius1,FFT_LPF_Radius2,FFT_LPF_Pow);

	if (OutputFileType>0 || OutputFileDepth != -1) {
	   int newtype = OutputFileType;
	   int newdepth = OutputFileDepth;

Print("OutputFileDepth = %d\n",newdepth);
	   if (newtype<0) newtype = img->type;
	   if (newdepth == -1) newdepth = img->depth;

Print("Convert to %d\n",newdepth);

	   img = ConvertToType(img,newtype,newdepth);
	   if (img==NULL) {
		Print("Cannot convert image '%s'(%s/%dbpp) to (%s/%dbpp)\n",
				img->src_fname,TypeName(img->type),img->depth,TypeName(newtype),newdepth);
		exit(1);
		}
	   }

	if (NoSave==0) {

	   if (!Quiet) Print("-> %s (%dx%dx%d) [%d]\n",img->dst_fname,img->dst_width,img->dst_height,img->depth,filecount);

	   if (! AllowWrite(img->dst_fname)) {
	         if (!Quiet) Print("Not overwriting output file %s\n",img->dst_fname);
	         }
	   else while (!WriteImage(img)) {
	      unlink(img->dst_fname);
	      Print("Write on image '%s' failed - sleeping for 10s before trying again\n",img->dst_fname);
	      Print("Press CTRL-C to exit or ninox will resume when disk space is available\n\n");
	      Usleep(1000000 * 10);
	      }
	   ++filecount;
	   }
	else {
	   Print("Not saved\n");
	   }

	// Are we writing images to standard output? IF so then use PGM format
	// and 8 bit data. This is used only for making avi's or other things where
	// 8 bit output is sufficient
	if (UsingStdout) {
	   int npix = img->width * img->height;

	   // We're streaming to stdout, so watch out for closed pipe
	   if (feof(stdout)) {
		Printf("Cannot write to standard output\n");
		exit(1);
		}

#ifdef MSWIN32
                // silly win32 has text mode stdio by default
           _setmode(fileno(stdout),O_BINARY);
#endif

           if (! fprintf(stdout,"P5\n%d %d\n255\n",img->width,img->height)) {
	 	Printf("Cannot write to stdout\n");
		exit(1);
		}

           if (img->depth == 16) {
		unsigned char *buf = (unsigned char *)Malloc(npix);
		unsigned short *udata = (unsigned short *)img->data;
	 	int i;

		for(i=0; i<npix; ++i) buf[i] = udata[i]>>8;
		if (! fwrite(buf,npix,1,stdout)) {
		   Printf("Write Image to stdout failed\n");
		   exit(1);
		   }
		free(buf);
		}
           else if (img->depth == 8) {
		if (! fwrite(img->data,npix,1,stdout)) {
		   Printf("Write Image to stdout failed\n");
		   exit(1);
		   }
		}
	   else {
		Print("WriteToStdout: Image depth %d not supported\n",img->depth);
		exit(1);
		}

           if (putc('\n',stdout) == EOF || fflush(stdout)) {
		Printf("Cannot write to standard output\n");
		exit(1);
		}
	   }

         // Possibly stack the input frame
       	if (StackFile && StackCount < StackMax) {
              stack_frame(img);
              StackCount++;
              if (!Quiet) Print("[Stack %d] ",StackCount);
              }

	// This has to be the last thing we do with the image since we're going to
	// draw all over it with text etc.

	if (DisplayFrames) {
	   ShowImage(img);
	   if (!Quiet) Print("[showimage]\n");
	   }

end:

	DestroyImage(img);
	free(CurrentFile); CurrentFile=NULL;
	Print("\n");

	first=0;
	return rval;
	}

// Number of consecutive pixels where we find a broken edge
#define DBF_TRIGGER 10

// Detect "broken frames", i.e. frames where one or more packets of data were lost
// during capture, so part of the image is horizontally shifted.
int
isBrokenFrame(struct Image *img)
	{
	int x,y,i;
	int w = img->width;
	int h = img->height;
	int depth = img->depth;
	unsigned char *data = (unsigned char *)img->data;
	unsigned short *udata = (unsigned short *)img->data;
	int left_edge,right_edge;
	int thresh = ThreshHold;
	int thresh2 = thresh * 1.5;
	static int x_avg = -1;
	static int y_avg = -1;
	int xa,ya,c;

	if (thresh2 > 250) thresh2 = 250;

	if (depth==16) { thresh <<= 8; thresh2 <<= 8; }

	// For planets, if they are off the screen then this is broken
	left_edge=right_edge=0;

	xa=ya=c=0;
	for(y=1; y<h-1; ++y) {
	   int o = y * w;
	   int count;

	   // Count number of columns where this pixel is above thresh2 and the pixel above is below thresh
	   // or vice versa
	   if (depth==8) {

		if (DBF_TYPE == DBF_PLANET) {
		   if (data[o] > thresh2) ++left_edge;
		   if (data[o+w-1] > thresh2) ++right_edge;
		   }

		count=0;
		for(++o,x=1; x<w-1; ++x,++o) {
		   int a = data[o-1]; a += data[o]; a += data[o+1]; a /= 3;
		   int b = data[o-w-1]; b += data[o-w]; b += data[o-w+1]; b /= 3;
		   if ((a>thresh2 && b<thresh) || (a<thresh && b>thresh2)) {
			if (++count > DBF_TRIGGER) {
				Print("Row %d triggers broken frame detect",y);
				return 1;
				}
		   	}
		   else count=0;
		   if (a>thresh) { xa += x; ya += y; c++; }
		   }
		}
	   else if (depth==16) {

		if (DBF_TYPE == DBF_PLANET) {
		   if (udata[o] > thresh2) ++left_edge;
		   if (udata[o+w-1] > thresh2) ++right_edge;
		   }

		for(++o,x=1; x<w-1; ++x,++o) {
		   int a = udata[o-1]; a += udata[o]; a += udata[o+1]; a /= 3;
		   int b = udata[o-w-1]; b += udata[o-w]; b += udata[o-w+1]; b /= 3;
		   if ((a>thresh2 && b<thresh) || (a<thresh && b>thresh2)) {
			if (++count > DBF_TRIGGER) {
				Print("Row %d triggers broken frame detect",y);
				return 1;
				}
		   	}
		   else count=0;

		   if (a>thresh) { xa += x; ya += y; c++; }
		   }
		}
	   else if (depth==24) {
		for(x=0,o*=3; x<w; ++x,o+=3) { // BGR
		   int a =  (double)data[o]     * 0.114 + (double)data[o+1]    *0.587 + (double)data[o+2]    *0.299;
		   int b = (double)data[o-w*3] * 0.114 + (double)data[o-w*3+1]*0.587 + (double)data[o-w*3+2]*0.299;

		   if ((a>thresh2 && b<thresh) || (a<thresh && b>thresh2)) {
			if (++count > DBF_TRIGGER) {
				Print("Row %d triggers broken frame detect",y);
				return 1;
				}
		   	}
		   else count=0;

		   if (a>thresh) { xa += x; ya += y; c++; }
		   }
		}

	   }

	if (DBF_TYPE == DBF_PLANET && (left_edge >= DBF_EDGE_COUNT || right_edge >= DBF_EDGE_COUNT)) {
	   if (!Quiet) Printf("Image laterally displaced, probably broken.\n");
	   return 1;
	   }

	if (c<100) {
	   Print("isBroken: Not enough pixels, cannot detect brokenness\n");
	   x_avg = 0;
	   y_avg = 0;
	   return 0;
	   }

	xa /= c;
	ya /= c;

	if (x_avg>0 && y_avg>0) {
	   int d = (x_avg-xa) * (x_avg-xa) + (y_avg-ya) * (y_avg-ya);
	   Print("Image centre at (%d,%d) compared to last (%d,%d) d=%d\n",xa,ya,x_avg,y_avg,(int)sqrt(d));
	   if (d > 400) {
	      Print("Image laterally displaced (average has moved)\n");
	      x_avg = xa;
	      y_avg = ya;
	      return 1;
	      }
	   }

	x_avg = xa;
	y_avg = ya;

	return 0;
	}
