/* Gxsm - Gnome X Scanning Microscopy
 * universal STM/AFM/SARLS/SPALEED/... controlling and
 * data analysis software
 * 
 * Copyright (C) 1999,2000,2001 Percy Zahl
 *
 * Authors: Percy Zahl <zahl@users.sf.net>
 * WWW Home: http://gxsm.sf.net
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */

/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 8 c-style: "K&R" -*- */

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <cmath>
#include <string.h>

#include <glib.h>
#include <png.h>
#include <popt.h>
#include "thumbpal.h"
#include "writepng.h"

using namespace std;

// ==============================================================
//
// based on the nctopng code
//
// Read UKSOFT2001 image/data generated by uview/LEEM
// and convert them to png image/thumbnail
// - read only required subgrid data (scaled down)
// - use direct, "more intelligent" autoscale
// - use palette
//
// ==============================================================

#ifndef WORDS_BIGENDIAN
# define WORDS_BIGENDIAN 0
#endif

typedef enum { UK2_READ_OK, UK2_OPEN_FAILED, UK2_FILE_NOT_VALID } UK2_STATUS;

#define THUMB_X 96 // Thumbnail max X size
#define THUMB_Y 91 // Thumbnail max Y size

// File header UKSOFT -- size: 104
typedef struct {
	gchar id[20];
	gint16 size;
	gint16 version;
	gint16 BitsPerPixel;
	gchar  align8[6];
	gint64 starttime;
	gint16 ImageWidth, ImageHeight;
	gint16 NrImages;
	gint16 spareShort;
	gchar spare[56];
} UKSOFT2001_FileHeader;

// Image header UKSOFT -- size: 48  -- fileheader.version < 5
typedef struct {
	gint16 size;
	gint16 version;
	gchar  align8[4];
	gint64 imagetime;
	gint32 LEEMdata1_source;
	gfloat LEEMdata1_data;
	gint16 spin;
	gint16 spareShort;
	gfloat LEEMdata2_data;
	gchar spare[16];
} UKSOFT2001_ImageHeader_lsv5;

// Image header UKSOFT -- size: 288  -- fileheader.version >= 5
typedef struct {
	gint16 size;
	gint16 version;
	gchar  align8[4];
	gint64 imagetime;
	gchar spare[8];
	gint16 spin;
	gint16 LEEMdataVersion;
	gchar LEEMdata[256];
	gchar space[4];
} UKSOFT2001_ImageHeader_gev5;

typedef struct {
	unsigned char source;
	gchar arg[64];
} UKSOFT2001_LEEMData;


static inline guint16 swap_u16 (guint16 x){
        return ((x & 0xff) << 8) | ((x >> 8) & 0xff);
}

static inline guint32 swap_u32 (guint32 x){
        return ((x & 0xff) << 24) | (((x >> 8)  & 0xff) << 16) | (((x >> 16) & 0xff) << 8) | ((x >> 24) & 0xff);
}

static inline guint64 swap_u64 (guint64 x){
        return ( (((x      ) & 0xff) << 56) | (((x >>  8)  & 0xff) << 48) | (((x >> 16) & 0xff) << 40) | (((x >> 24) & 0xff) << 32)	
	       | (((x >> 32) & 0xff) << 24) | (((x >> 40)  & 0xff) << 16) | (((x >> 48) & 0xff) <<  8) | (((x >> 56) & 0xff)      ) );
}
// -mno-strict-align ??? 
static inline gfloat check_and_swap_float (gfloat *x){
	if (WORDS_BIGENDIAN){
		guint32 u32 = * ((guint32*)x);
		gfloat   f;
		u32 = swap_u32 (u32);
		f = * ((gfloat*)(&u32));
		return f;
	} else return *x;
}




class raw_image{
public:
	raw_image(){ rgb=NULL; };
	virtual ~raw_image(){ 
		if(rgb){
			for (int i=0; i<ny; delete[] rgb[i++]);
			delete[] rgb; 
		}
	};

	int width() { return nx; };
	int height() { return ny; };
	void calc_line_regress(int y, double &slope, double &offset){
		//
		// OutputFeld[i] = InputFeld[i] - (slope * i + offset)
		//
		double sumi, sumy, sumiy, sumyq, sumiq;
		int i, istep;
		int Xmax, Xmin;
		double a, n, imean, ymean;
			
		sumi = sumiq = sumy = sumyq = sumiy = 0.0;
		Xmax = nx-nx/10; // etwas Abstand (10%) vom Rand !
		Xmin = nx/10;
		istep = 1;
		for (i=Xmin; i < Xmax; i+=istep) { /* Rev 2 */
			sumi   += (double)i;
			sumiq  += (double)i*(double)i;
			a =  rowdata[y][i];
			sumy   += a;
			sumyq  += a * a;
			sumiy  += a * (double)i;
		}
		n = (double)((Xmax-Xmin)/istep);
		if ((Xmax-Xmin) % istep != 0) n += 1.0;
		imean = sumi / n;
		ymean = sumy / n;
		slope  = (sumiy- n * imean * ymean ) / (sumiq - n * imean * imean);
		offset = ymean - slope * imean;
	};
	double soft(int i, int j, double lim=0){
/*
		int a,b,c,d;
		a = max (0, i-1);
		b = min (i+1, nx-1);
		c = max (0, j-1);
		d = min (j+1, ny-1);
*/
		return rowdata[j][i];
	}
	void find_soft_min_max (double &min, double &max){
		min = max = soft(0,0);
		for (int i=0; i<ny; ++i)
			for (int j=0; j<nx; ++j){
				double v = soft (j,i);
				if (min > v) min = v;
				if (max < v) max = v;
			}
	};
	void quick_rgb(int linreg=TRUE) {
			if (rgb){
				for (int i=0; i<ny; delete[] rgb[i++]);
				delete[] rgb;
			}
			rgb = new unsigned char* [ny];
			for (int i=0; i<ny; rgb[i++] = new unsigned char[3*nx]);

			if (linreg){
					// Calc and Apply "Quick"
					for (int i=0; i<ny; ++i){
							double a,b;
							calc_line_regress (i, a, b);
							for (int j=0; j<nx; ++j)
									rowdata[i][j] -= j*a+b;
					}
			}

			// Find Scale
			double min, max, range;
			find_soft_min_max (min, max);
			range = max-min;
			for (int i=0; i<ny; ++i)
				for (int j=0, k=0; j<nx; ++j){
					int idx = 3*(int)((rowdata[i][j]-min)/range*PALETTE_ENTRIES+0.5);
					rgb[i][k++] = thumb_palette[idx++];
					rgb[i][k++] = thumb_palette[idx++];
					rgb[i][k++] = thumb_palette[idx];
				}
			
	};
	unsigned char **row_rgb_pointers() { return rgb; };

protected:
	int nx, ny; // image size
	int x0, y0, w0; // offset, data width
	int onx, ony; // original data size of NC-file
	double **rowdata;
	unsigned char **rgb;
};

void swap_ushort (unsigned short *addr){
	unsigned short temp1, temp2;
	temp1 = temp2 = *addr;
	*addr = ((temp2 & 0xFF) << 8) | ((temp1 >> 8) & 0xFF);
}

void swap_short (short *addr){
	unsigned short temp1, temp2;
	temp1 = temp2 = *addr;
	*addr = ((temp2 & 0xFF) << 8) | ((temp1 >> 8) & 0xFF);
}


class uk2_image : public raw_image {
public:
	uk2_image (const gchar *file_name, int thumb, int new_x, int x_off, int y_off, int width) {
		ifstream f;
		int image_number=0; 
		int index_value=0;
		int index_time=0;
		int skip=0, video=0;
		int pflg = 1; // dump info
		int max_index_value=1;
		int offset_index_time=0;
		int max_index_time=1;	
		int step_index_time=1;	
		UKSOFT2001_FileHeader uks_fileheader;
		UKSOFT2001_ImageHeader_lsv5 uks_imageheader_lsv5;
		UKSOFT2001_ImageHeader_gev5 uks_imageheader_gev5;
		
		// specific LEEM2000 data taken from header
		double ImageTime    = 0.; // ImageTime [???]
		double StartVoltage = 0.; // #Module #38 [V]
		double Temperatur   = 0.; // #Module #39 [C]
		double Temp_Offset  = 0.; // korrection
		double Pressure11   = 0.; // #Varian #106 [Torr]
		double FieldOfView  = 10.; // #FOV #110 [um]
		int FOV_assumed=0;
		double IllumDeflX   = 0.; // #2 Illumination Deflect. X [mA]
		double IllumDeflY   = 0.; // #3 Illumination Deflect. Y [mA]

		x0 = x_off; y0 = y_off;

		// Am I resposible for that file, is it a "UK2001-Dat" format ?

		f.open(file_name, ios::in);
		if (!f.good()){
			state = UK2_OPEN_FAILED;
			return;
		}

		// Checking resposibility for this file as good as possible, use
		// extension(s) (most simple), magic numbers, etc.
		
//		cout << "UKSOFT::: importing from >" << fname << "< [" << index_value << ", " << index_time << "]" << endl;
		// now start importing -----------------------------------------

		f.read((char*)&uks_fileheader, sizeof(uks_fileheader)); // read header
		// file type sanity check
		if( strncmp (uks_fileheader.id, "UKSOFT2001", 10)){ // check version and type
			uks_fileheader.id[19]=0; // for safety
			g_print ("Sorry [%s] is not a UKSOFT2001 file: incorrect signature [%s]\n", file_name, uks_fileheader.id);
			f.close ();
			state = UK2_FILE_NOT_VALID;
			return;
		}

		if (WORDS_BIGENDIAN){
			uks_fileheader.size    = swap_u16(uks_fileheader.size);
			uks_fileheader.version = swap_u16(uks_fileheader.version);
			uks_fileheader.BitsPerPixel = swap_u16(uks_fileheader.BitsPerPixel);
			uks_fileheader.starttime   = swap_u64(uks_fileheader.starttime);
			uks_fileheader.ImageWidth  = swap_u16(uks_fileheader.ImageWidth);
			uks_fileheader.ImageHeight = swap_u16(uks_fileheader.ImageHeight);
			uks_fileheader.NrImages    = swap_u16(uks_fileheader.NrImages);
			uks_fileheader.spareShort  = swap_u16(uks_fileheader.spareShort);
		}

		if (!strncasecmp (file_name+strlen(file_name)-4,".dav", 4)){
			video=1;
			max_index_value = 1;
			g_print ("UKSOFT 2001 CCD Video Import [%d]\n", max_index_value);
			uks_fileheader.NrImages = max_index_time;
			skip = step_index_time-1;
		}
 
//		for (image_number = 0; image_number < uks_fileheader.NrImages && f.good(); ++image_number){
		// only first frame for preview
		for (image_number = 0; image_number < 1 && f.good(); ++image_number){

			if (uks_fileheader.version < 5){
				f.read((char*)&uks_imageheader_lsv5, sizeof(uks_imageheader_lsv5)); // read image header
				if (WORDS_BIGENDIAN){
					uks_imageheader_lsv5.size    = swap_u16(uks_imageheader_lsv5.size);
					uks_imageheader_lsv5.version = swap_u16(uks_imageheader_lsv5.version);
					uks_imageheader_lsv5.imagetime = swap_u64(uks_imageheader_lsv5.imagetime);
					uks_imageheader_lsv5.spin    = swap_u16(uks_imageheader_lsv5.spin);
				}
			} else {
				f.read((char*)&uks_imageheader_gev5, sizeof(uks_imageheader_gev5)); // read image header
				if (WORDS_BIGENDIAN){
					uks_imageheader_gev5.size    = swap_u16(uks_imageheader_gev5.size);
					uks_imageheader_gev5.version = swap_u16(uks_imageheader_gev5.version);
					uks_imageheader_gev5.imagetime = swap_u64(uks_imageheader_gev5.imagetime);
					uks_imageheader_gev5.spin    = swap_u16(uks_imageheader_gev5.spin);
					uks_imageheader_gev5.LEEMdataVersion = swap_u16(uks_imageheader_gev5.LEEMdataVersion);
				}
			}


#define PRINT_HDR_INFO(X) { if (pflg) { cout << X << endl; } }

			if (1){
				PRINT_HDR_INFO ("UKS-Image Number....:" << image_number);
				PRINT_HDR_INFO ("UKS-FH: ***sizeof***=" << sizeof(uks_fileheader));
				PRINT_HDR_INFO ("UKS-FH: Id          =" << uks_fileheader.id);
				PRINT_HDR_INFO ("UKS-FH: Size        =" << uks_fileheader.size);
				PRINT_HDR_INFO ("UKS-FH: Version     =" << uks_fileheader.version);
				PRINT_HDR_INFO ("UKS-FH: BitsPerPixel=" << uks_fileheader.BitsPerPixel);
				PRINT_HDR_INFO ("UKS-FH: starttime   =" << uks_fileheader.starttime);
				PRINT_HDR_INFO ("UKS-FH: ImageWidth  =" << uks_fileheader.ImageWidth);
				PRINT_HDR_INFO ("UKS-FH: ImageHeight =" << uks_fileheader.ImageHeight);
				PRINT_HDR_INFO ("UKS-FH: NrImages    =" << uks_fileheader.NrImages);
				PRINT_HDR_INFO ("UKS-FH: spareShort  =" << uks_fileheader.spareShort);
				if (uks_fileheader.version < 5){
					PRINT_HDR_INFO ("UKS-IH*: ***sizeof**=" << sizeof(uks_imageheader_lsv5));
					PRINT_HDR_INFO ("UKS-IH*: Size       =" << uks_imageheader_lsv5.size);
					PRINT_HDR_INFO ("UKS-IH*: Version    =" << uks_imageheader_lsv5.version);
					PRINT_HDR_INFO ("UKS-IH*: imagetime  =" << uks_imageheader_lsv5.imagetime);
					ImageTime = (double)uks_imageheader_lsv5.imagetime;
				} else {
					PRINT_HDR_INFO ("UKS-IH: ***sizeof***=" << sizeof(uks_imageheader_gev5));
					PRINT_HDR_INFO ("UKS-IH: Size        =" << uks_imageheader_gev5.size);
					PRINT_HDR_INFO ("UKS-IH: Version     =" << uks_imageheader_gev5.version);
					PRINT_HDR_INFO ("UKS-IH: imagetime   =" << uks_imageheader_gev5.imagetime);
					PRINT_HDR_INFO ("UKS-IH: spin        =" << uks_imageheader_gev5.spin);
					PRINT_HDR_INFO ("UKS-IH: LEEMdataVers=" << uks_imageheader_gev5.LEEMdataVersion);
					ImageTime = (double)(uks_imageheader_gev5.imagetime - 128137000000000000LL); // willkuerlicher offset
					UKSOFT2001_LEEMData *ld;
					gchar *ld_ptr = (gchar*)&(uks_imageheader_gev5.LEEMdata);
					gchar *ld_ptr_end = ld_ptr + 256;
					for (ld = (UKSOFT2001_LEEMData*) ld_ptr; ld_ptr < ld_ptr_end; ++ld_ptr){
						size_t l, l2;
						float* xy;
						ld = (UKSOFT2001_LEEMData*) ld_ptr;
// 					if (ld->source < 0xff){
// 						for (int hx=0; hx<16; ++hx) cout << ((int)(*((unsigned char*)(&(ld->source) + hx)))) << " "; 
// 						cout << endl;
// 						for (int hx=0; hx<16; ++hx){
// 							float ff;
// 							xy = (float*)(&(ld->source) + hx);
// 							ff = check_and_swap_float (&xy[0]);
// 							cout << ff << " "; 
// 						}
// 						cout << endl;
// 					}
						switch (ld->source){
						case 0xff: 
							break;
						case 100:
							xy = (float*) (ld->arg);
							xy[0] = check_and_swap_float (&xy[0]);
							xy[1] = check_and_swap_float (&xy[1]);
							PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source) << "] Micrometer XY: " << xy[0] << ", " << xy[1]);
							ld_ptr += 2*sizeof(float);
							break;
						case 101:
							l = strlen(ld->arg)+1;
							PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source) << "] FOV: " << ld->arg);
							ld_ptr += l;
							break;
						case 102:
							xy = (float*) (ld->arg);
							xy[0] = check_and_swap_float (&xy[0]);
							PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source) << "] Varian Gauge #1: " << xy[0]);
							ld_ptr += 1*sizeof(float);
							break;
						case 103:
							xy = (float*) (ld->arg);
							xy[0] = check_and_swap_float (&xy[0]);
							PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source) << "] Varian Gauge #2: " << xy[0]);
							ld_ptr += 1*sizeof(float);
							break;
						case 104:
							xy = (float*) (ld->arg);
							xy[0] = check_and_swap_float (&xy[0]);
							PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source) << "] Camera Exposure Time [ms]: " << xy[0]);
							ld_ptr += 1*sizeof(float);
							break;
						case 105:
							l = strlen(ld->arg)+1;
							PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source) << "] Title: " << ld->arg);
							ld_ptr += l;
							break;
						case 106:
							l = strlen(ld->arg)+1;
							l2 = strlen(ld->arg + l)+1;
							xy = (float*) (ld->arg + l + l2);
							xy[0] = check_and_swap_float (&xy[0]);
							PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source) << "] Varian#1 Gauge#1: " << ld->arg << "[" << (ld->arg + l) << "]: " << xy[0]);
							Pressure11 = xy[0]; // #Varian #106 [Torr]
							ld_ptr += l + l2 + sizeof(float);
							break;
						case 107:
							l = strlen(ld->arg)+1;
							l2 = strlen(ld->arg + l)+1;
							xy = (float*) (ld->arg + l + l2);
							xy[0] = check_and_swap_float (&xy[0]);
							PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source) << "] Varian#1 Gauge#2: " << ld->arg << "[" << (ld->arg + l) << "]: " << xy[0]);
							ld_ptr += l + l2 + sizeof(float);
							break;
						case 108:
							l = strlen(ld->arg)+1;
							l2 = strlen(ld->arg + l)+1;
							xy = (float*) (ld->arg + l + l2);
							xy[0] = check_and_swap_float (&xy[0]);
							PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source) << "] Varian#2 Gauge#1: " << ld->arg << "[" << (ld->arg + l) << "]: " << xy[0]);
							ld_ptr += l + l2 + sizeof(float);
							break;
						case 109:
							l = strlen(ld->arg)+1;
							l2 = strlen(ld->arg + l)+1;
							xy = (float*) (ld->arg + l + l2);
							xy[0] = check_and_swap_float (&xy[0]);
							PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source) << "] Varian#2 Gauge#2: " << ld->arg << "[" << (ld->arg + l) << "]: " << xy[0]);
							ld_ptr += l + l2 + sizeof(float);
							break;
						case 110:
							l = strlen(ld->arg)+1;
							xy = (float*) (ld->arg + l);
							xy[0] = check_and_swap_float (&xy[0]);
							PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source) << "] FOV, Camera to FOV cal. factor: " << ld->arg << ", " << xy[0]);
							FieldOfView = atof (ld->arg); // #FOV #110 [um]
							if (FieldOfView < 0.1){ // some files are messed up w FOV=0, assume 10um.
								FieldOfView = 10.;
								FOV_assumed = 1;
								PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source)
										<< "] FOV assumed: " << FOV_assumed						
										<< " FOV set to: " << FieldOfView
										);
							}
							ld_ptr += l + sizeof(float);
							break;
						case 111:
							xy = (float*) (ld->arg);
							xy[0] = check_and_swap_float (&xy[0]);
							xy[1] = check_and_swap_float (&xy[1]);
							PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source) << "] Phi, Theta: " << xy[0] << ", " << xy[1]);
							ld_ptr += 2*sizeof(float);
							break;
						default: 
							if(ld->source < 100){
								l = strlen(ld->arg)+1;
								xy = (float*) (ld->arg + l);
								xy[0] = check_and_swap_float (&xy[0]);
								PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source) << "] LEEM2000 module#"<< (int)(ld->source) << " '" << ld->arg << "': " << xy[0]);

								if (ld->source == 2)
									IllumDeflX = xy[0]; // #Module #2 [mA]
								if (ld->source == 3)
									IllumDeflY = xy[0]; // #Module #3 [mA]

								if (ld->source == 38)
									StartVoltage = xy[0]; // #Module #38 [V]

								if (ld->source == 39)
									Temperatur   = xy[0] + Temp_Offset; // #Module #39 [C] + User Temp. Offset

								ld_ptr += l + sizeof(float);
							} else
								PRINT_HDR_INFO ("UKS-IH: LEEMdata[" << (int)(ld->source) << "] ??? (undocumented by 20070131) ");
							break;
						}
					}
				}
			}

			if (index_value == 0 && index_time == 0){

				// Fill Scan Parameter
//				scan->data.s.ntimes = max_index_time;
//				scan->data.s.nvalues = max_index_value;

//				XsmRescourceManager xrm("UKSOFT2001_IM_EXPORT");
				// assume this size 528 x 528 or 544x520 for 2x2 binning
				onx = uks_fileheader.ImageWidth;
				ony = uks_fileheader.ImageHeight;
//				scan->data.s.dx = FieldOfView*1e4 / (scan->data.s.nx - 1);
//				scan->data.s.dy = scan->data.s.dx; // Aspect 1:1 from Channelplate
//				scan->data.s.dz = 1.;
//				scan->data.s.rx = scan->data.s.nx*scan->data.s.dx;
//				scan->data.s.ry = scan->data.s.ny*scan->data.s.dy;
//				scan->data.s.rz = 4096.0;
//				scan->data.s.x0 = 0;
//				scan->data.s.y0 = 0;
//				scan->data.s.alpha = 0.;
//				scan->data.display.bright = 0.;
//				scan->data.display.vrange_z = 4096.;
//				scan->data.display.voffset_z = 0.;

//				scan->data.s.GateTime   = 1.;
//				scan->data.display.cnttime = 1.;
//				scan->data.s.dz = 1./scan->data.display.cnttime;
//				scan->data.display.cpshigh         = 4096.;
//				scan->data.display.cpslow          = 0.;
  
//				scan->mem2d->Resize (scan->data.s.nx, scan->data.s.ny, max_index_value, ZD_SHORT);
//				scan->data.orgmode = SCAN_ORG_CENTER;
//				scan->mem2d->data->MkXLookup (-scan->data.s.rx/2., scan->data.s.rx/2.);
//				scan->mem2d->data->MkYLookup (-scan->data.s.ry/2., scan->data.s.ry/2.);
//				scan->mem2d->data->MkVLookup (start_value, start_value+(max_index_value-1)*step_value);

//				g_string_append_printf (FileList, "Temp. Offset User: %6.1f C\n", Temp_Offset);
			}

			if (video){
				if (offset_index_time > 0){
					--offset_index_time;
					f.seekg(uks_fileheader.ImageWidth*uks_fileheader.ImageHeight*2, ios::cur);
					continue;
				}
				if (skip > 1){
					--skip;
					f.seekg(uks_fileheader.ImageWidth*uks_fileheader.ImageHeight*2, ios::cur);
					continue;
				} else 
					skip = step_index_time-1;
			}

			g_print ("%s: %18.0f us, %5.1f V, IlDfxy: %5.2f mA, %5.2f mA, %6.1f C, %4g Torr, %g um\n", file_name, ImageTime, StartVoltage, IllumDeflX, IllumDeflY, Temperatur, Pressure11, FieldOfView);
//			scan->mem2d->SetLayer(index_value);
//			scan->data.ui.SetComment (FileList->str);

			
			// compute scale and offsets
			//
			if (width>0){
				w0=width;
				nx=new_x;
				ny=new_x;
			}else{
				w0=onx;
				if (thumb){
					if(onx < ony*THUMB_X/THUMB_Y){
						ny=THUMB_Y;
						nx=onx*ny/ony;
					}else{
						nx=THUMB_X;
						ny=ony*nx/onx;
					}
				}else{
					if (new_x){ // scale to new X w aspect
						nx=new_x;
						ny=ony*nx/onx;
					}else{
						nx=onx; 
						ny=ony;
					}
				}
			}
			if (x0+nx >= onx) x0=0; // fallback
			if (y0+ny >= ony) y0=0; // fallback
			

			//prepare memory
			rowdata = new double* [ny];
			for (int i=0; i<ny; rowdata[i++] = new double [nx]);
			
			//
			// read data
			//

			const unsigned long uk2_data_offset = f.tellg();
			double scale = (double)nx/(double)w0;
			short *row = new short[onx];
			short data;
			
			for (int y=0; y<ny; y++){
				f.seekg((y0+(int)((double)y/scale+.5))*onx*sizeof(short) + uk2_data_offset);
				f.read((char*)row, onx*sizeof(short));
				for (int i=0; i<nx; ++i){
					data = row[x0+(int)((double)i/scale+.5)];
					if (WORDS_BIGENDIAN)
						swap_short (&data);
					rowdata[y][i] = (double)data;
				}
			}
			
			f.close ();
		}
		state = UK2_READ_OK;
	};

	const UK2_STATUS status(){
		return state;
	};
	

	virtual ~uk2_image() {
		for (int i=0; i<ny; delete [] rowdata[i++]);
		delete [] rowdata;
	};

private:
	UK2_STATUS state;
};


int write_png(gchar *file_name, raw_image *img){
// see here for pnglib docu:
// http://www.libpng.org/pub/png/libpng-manual.html

	mainprog_info m;

	m.gamma   = 1.;
	m.width   = img->width();
	m.height  = img->height();
	m.modtime = time(NULL);
	m.infile  = NULL;
	if (! (m.outfile = fopen(file_name, "wb")))
		return -1;
	m.row_pointers = img->row_rgb_pointers();
	m.title  = g_strdup ("uksofttopng");
	m.author = g_strdup ("P. Zahl");
	m.desc   = g_strdup ("uksoft/leem data to png");
	m.copyright = g_strdup ("GPL");
	m.email   = g_strdup ("zahl@users.sourceforge.net");
	m.url     = g_strdup ("http://gxsm.sf.net");
	m.have_bg   = FALSE;
	m.have_time = FALSE;
	m.have_text = FALSE;
	m.pnmtype   = 6; // TYPE_RGB
	m.sample_depth = 8;
	m.interlaced= FALSE;

	writepng_init (&m);
	writepng_encode_image (&m);
	writepng_encode_finish (&m);
	writepng_cleanup (&m);

	g_free (m.title);
	g_free (m.author);
	g_free (m.desc);
	g_free (m.copyright);
	g_free (m.email);
	g_free (m.url);

	return 0;
}


/* ****************************
              main
   ***************************/
int main(int argc, const char *argv[]) {
	int thumb = 1;
	int new_x = 0;
	int x_off = 0, y_off = 0, width = 0;	
	int verbose = 0;
	int noquick = 0;
	int help = 0;
	gchar *filename;
	gchar *destinationfilename;
	uk2_image *img = NULL;
	poptContext optCon; 

	struct poptOption optionsTable[] = {
		{"x-offset",'x',POPT_ARG_INT,&x_off,0,"x-offset",NULL},
		{"y-offset",'y',POPT_ARG_INT,&y_off,0,"y-offset",NULL},
		{"width",'w',POPT_ARG_INT,&width,0,"width",NULL},
		{"size",'s',POPT_ARG_INT,&new_x,0,"Size in x-direction.",NULL},
		{"verbose",'v',POPT_ARG_NONE,&verbose,1,"Display information.",NULL},
		{"noquick",'n',POPT_ARG_NONE,&noquick,1,"No-quick",NULL},
		{"help",'h',POPT_ARG_NONE,&help,1,"Print help.",NULL},
		{ NULL, 0, 0, NULL, 0 }
	};

	optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
	poptSetOtherOptionHelp(optCon, "gmefile.t[fb]? [outfile.png]");
	while (poptGetNextOpt(optCon) >= 0) {;}	//Now parse until no more options.

	if ((argc < 2 )||(help)) { 
		poptPrintHelp(optCon, stderr, 0);
		exit(1);
	}
		
	filename = g_strdup(poptGetArg(optCon));
	destinationfilename = g_strdup(poptGetArg(optCon));

	if (destinationfilename == NULL){
		destinationfilename = g_strjoin(NULL, filename, ".png", NULL);
		// using simple join. if you need more sophisticated
		// have a look at 'mmv' for suffix handling.
	}

	if(verbose){
		if (new_x == 0)	
			cout << "Thumbnail-size" << endl;
		else
			cout << "Rescaling to new x-size = " << new_x << endl;
		cout << "Sourcefile: " << filename << endl;
		cout << "Destinationfile: " << destinationfilename << endl;
	}

	if (new_x > 0)
		thumb = 0;
	
	if ( x_off + y_off + width > 0){
		if ( (x_off==0) || (y_off==0) || (width==0) ) {
			cout << "Please use -x and -y and -w together."<< endl;
			exit(-1);
		}
		if(verbose){
			cout << "Offset: " << x_off << " x " << y_off << endl;
			cout << "Width set to: " << width << endl;
		}
	}
		
	// load & rescale image
	img = new uk2_image(filename, thumb, new_x, x_off, y_off, width);

	switch (img->status()){
		case UK2_READ_OK: break;
		case UK2_OPEN_FAILED: 
		        cerr << "Error opening Gme file >" << filename << "<" << endl; 
			exit(-1);
			break;
		case UK2_FILE_NOT_VALID:
		        cerr << "invalid Gme file >" << filename << "<" << endl; 
		        exit(-1);
			break;
	}
		
	if  (!img){
		cerr << "Error while creating image from Gme file." << endl;
		exit(-1);
	}
		
	if (verbose)
		cout << "Converting ..." << endl; 
		
	if (noquick){
		cout << "No Quick LR" << endl; 
		img->quick_rgb(FALSE);
	}else
		img->quick_rgb();

	if (verbose)
		cout << "Writing >" << destinationfilename << "<" << endl; 
		
	write_png(destinationfilename, img);

	if(verbose)
		cout << "Writing complete." << endl;
		
	g_free(filename);
	g_free(destinationfilename);
	poptFreeContext(optCon);
	exit(0);
}
