#include <stdio.h>
#include <stdlib.h>
#include <math.h>

/* compile this file with: cc -o osc2sgi osc2sgi.c -lm */

#define TRUE 1
#define FALSE 0

/* 

    Note: denzo's convention of X and Y is reversed from the one used here
	  in denzo, the slow axis (up and down) is X and the fast axis (side to side) is Y

	  If you want to zoom in on a spot at a denzo (X,Y) of (800,900) (in the .x file)
	  use -box 880 780  920 820 -zoom 10


 */

/* the following used to be in swapper.h */
typedef union
{
        float real;
        unsigned char string[4];
        unsigned int integer;
} FOURBYTES;

/* function prototypes */
FOURBYTES fix_int(FOURBYTES wrong_order);
FOURBYTES fix_float(FOURBYTES wrong_order);

void fix_int_list(FOURBYTES *wrong_order, int howmany);
void fix_float_list(FOURBYTES *wrong_order, int howmany);

void nospaces(char *string);


/* the following used to be in rgbgen.h */
typedef struct _SGIHeader
{
  short Magic;          /* Identification number (474) */
  char storage;         /* Compression flag */
  char bpc;             /* Bytes per pixel */
  unsigned short int dimension;       /* Number of image dimensions */
  unsigned short int xsize;           /* Width of image in pixels */
  unsigned short int ysize;           /* Height of image in pixels */
  unsigned short int zsize;           /* Number of bit planes */
  long pixmin;          /* Smallest pixel value */
  long pixmax;          /* Largest pixel value */
  char dummy1[4];       /* Not used */
  char name[80];        /* Name of image */
  long colormap;        /* Format of pixel data */
  char dummy2[404];     /* Not used */
} SGIHEAD;



/* Raxis frame data structures */
typedef enum {UNIX, VMS, UNKNOWN} byte_order;
byte_order file_type = UNKNOWN;
unsigned char header[4096];

/* Raxis frame handling routines */
FILE *GetFrame(char *filename);
char *GetString(char *buffer, int position, int length);
float GetFloat(int position);
int GetInt(int position);



main(int argv, char** argc)
{
    /* exit status */
    int return_code = 0;

    /* utility variables */
    int i;
    char *filename;
    FILE *frame = NULL;
    unsigned long int **image;

    /* frame information variables */
    int record_size;
    int num_records;
    int pixels_per_line;
    float eight;

    /* indicies */
    int x, z;
    int overloads = 0;
    
    /* limits */
    int high = 0;
    int low = 0xFFFF;
    double sum = 0;
    double sigma = 0;
    double scale = 0;
    double zoom = 1;
    int negate  = 0;
    int start_x = 0;
    int start_y = 0;
    int  stop_x = 0;
    int  stop_y = 0;
    
    /* storage */
    short *record;
    short *swapped;
    short *temp;

    
    /* output RGB stuff */
    SGIHEAD        SGIhead;
    unsigned char *SGIdata;
    FILE *rgbout = NULL;
    char outfilename[256] = "new.rgb";

    /* begin progam here */
    printf("OSC2SGI 1.0.1\t\tby James Holton 3-4-99\n\n");

    /* check argument list */
    for(i=1; i<argv; ++i)
    {
	if(strstr(argc[i], ".osc"))
	{
	    /* filename specified */
	    if(frame == NULL)
	    {
		/* mimic input filename for output */
		filename = argc[i];
		while(strchr(filename, '/') != NULL)
		{
		    /* how come basename doesn't work? */
		    filename = (char *) strchr(filename, '/') + 1;
		}

		strncpy(outfilename, filename, strlen(filename)-4);
		strcat(outfilename, ".sgi");

		filename = argc[i];
		frame = GetFrame(filename);
	    }
	}
	if(strstr(argc[i], ".rgb") || strstr(argc[i], ".sgi"))
	{
	    strcpy(outfilename, argc[i]);
	}
	if(argc[i][0] == '-')
	{
	    /* option specified */
	    if(strstr(argc[i], "-negate")) negate = 1;
	    if(strstr(argc[i], "-invert")) negate = 1;
	    if(strstr(argc[i], "-scale") && (argv >= (i+1)))
	    {
		scale = atof(argc[i+1]);
	    }
	    if(strstr(argc[i], "-zoom") && (argv >= (i+1)))
	    {
		zoom = atof(argc[i+1]);
	    }
	    if(strstr(argc[i], "-box") && (argv >= (i+4)))
	    {
		start_x = atol(argc[i+1]);
		start_y = atol(argc[i+2]);
		 stop_x = atol(argc[i+3]);
		 stop_y = atol(argc[i+4]);
	    }
	}
    }
    
    /* now process the given file */
    if((file_type != UNKNOWN)&&(frame != NULL))
    {
    	/* Print intentions */
    	printf("converting %s to %s\n", filename, outfilename);
    	printf("scaling size by %g\n", zoom);
    	if(scale == 0)
    	{
	    printf("autoscaling intensity \n");
    	}
    	else
    	{
	    printf("intensity scale set to %g\n", scale);
    	}
    	printf("\n");

	/* Filename */
	printf("reading %s ", filename);

	record_size = GetInt(784);
	num_records = GetInt(788);
	pixels_per_line = GetInt(768);
	eight = GetFloat(800);

	if(stop_x == 0) stop_x = pixels_per_line;
	if(stop_y == 0) stop_y = num_records;
	if(stop_x < start_x) {i = start_x; start_x = stop_x; stop_x = i;}
	if(stop_y < start_y) {i = start_y; start_y = stop_y; stop_y = i;}
	
	printf("box from (%d,%d) to (%d,%d)\n", start_x, start_y, stop_x, stop_y);
    
	record = calloc(1, record_size);

	/* allocate space for memeory-resident image */
	image = (unsigned long int **) calloc(sizeof(unsigned long int *), stop_y - start_y);

	/* need a little extra memory for swapping bytes */
	if(file_type == VMS)
	{
	    swapped = calloc(1, record_size);
	    printf("swapping bytes...\n");
	}

	/* load appropriate records (line) into memory */
	for(z = 0; z < (stop_y - start_y); ++z)
	{
	    /* position file at beginning of this DATA record */
	    fseek(frame, (z+start_y+1)*record_size, SEEK_SET);
	
	    /* read the record into memory */
	    fread(record, 1, record_size, frame);
	
	    /* Account for byte swapping */
	    if(file_type == VMS)
	    {
		/* preserve location of read-in pixels */
		temp = record;
	    
		/* swab() can only COPY swapped bytes */
		swab((char*)record, (char*)swapped, record_size);
		record = swapped;
	    
		/* exchange buffers for next time */
		swapped = temp;
	    }

	    /* allocate space for real pixel values */
	    image[z] = (unsigned long int *) calloc(sizeof(unsigned long int), stop_x - start_x);
	    
	    /* promote pixel words to actual values */
	    for(x = 0; x < (stop_x - start_x); ++x)
	    {
		i = start_x + x;
		/* initialize from words in file */
		if(record[i] == -1)
		{
		    ++overloads;
		    
		    image[z][x] = 262144;
		}
		if(record[i] >= 0)
		{
		    image[z][x] = record[i];
		}
		if(record[i] < -1)
		{
		    image[z][x] = (unsigned long int) eight*(0x8000+(record[i]));
	    
		    /* this should never happen */
		    if(record[i] < -28672)
		    printf("oddball pixel!: (%4d, %4d) = %4d\n", x, z-1, record[i]);
		}

		/* debug *
		printf("reading (%d,%d) = ", z, x);
		printf("%d  \n", image[z][x]);  /*  */

		/* now collect statistics */
		sigma += ( (double) image[z][x]) * ((double) image[z][x]);
	    }
	}
	
	/* image has now been completely read into memory */
	fclose(frame);
	
	/* do some statistics */
	sigma /= (double) ((stop_x - start_x) * (stop_y - start_y));
	sigma = sqrt(sigma);
    
	/* scale output image to 5-sigma cuttoff */
	if(scale == 0)
	{
	    scale = 256.0/(5*sigma);
	    printf("intensity scale set to %g\n", scale);
	}
	
	
	
	
	
	/* construct RGB image header */
	SGIhead.Magic = 474; /* always 474 for RGB */
	SGIhead.storage = 0; /* uncompressed */
	SGIhead.bpc = 1; /* one byte per pixel (channel) */
	SGIhead.dimension = 2; /* 2D image */
	SGIhead.xsize = (stop_x - start_x)*zoom; /*  */
	SGIhead.ysize = (stop_y - start_y)*zoom; /*  */
	SGIhead.zsize = 1; /* one z-pixel (* channels) */
	SGIdata = (unsigned char *) calloc(SGIhead.xsize*SGIhead.ysize*SGIhead.zsize, SGIhead.bpc);
	strncpy(SGIhead.dummy1, SGIdata, 4);
	strncpy(SGIhead.dummy2, SGIdata, 404);
	SGIhead.pixmin = 0; /* minimum pixel value */
	SGIhead.pixmax = 255; /* maximum pixel value */
	strcpy(SGIhead.name, "no name"); /* set to origional image name */
	SGIhead.colormap = 0; /* grayscale/RGB */
    
	/* convert and copy all image data into new RGB file */
	printf("transforming data ...\n");
	for(z = 0; z < SGIhead.ysize; ++z)
	{
	    for(x = 0; x < SGIhead.xsize; ++x)
	    {
		/* debug *
		printf("xform (%d,%d) -> (%d,%d) = ", (int)(z/zoom), (int)(x/zoom), z, x);
		printf("%d\n", image[(int)(z/zoom)][(int)(x/zoom)]); /*  */

		/* transform frame image */
		sum = scale*image[(int)(z/zoom)][(int)(x/zoom)];
		if(sum > 255) sum = 255;
		if(negate) sum = 255-sum;
		
		SGIdata[(z*SGIhead.xsize)+x] = (unsigned char) sum;
	    }
	}
    
	/* write out the RGB image */
	rgbout = fopen(outfilename, "wb");
	if(rgbout != NULL)
	{
	    printf("writing %s\n", outfilename);
	    fwrite(&SGIhead, 512, 1, rgbout);
	    fwrite(SGIdata, SGIhead.bpc, SGIhead.xsize*SGIhead.ysize*SGIhead.zsize, rgbout);
	    fclose(rgbout);
	}
	else
	{
	    printf("ERROR: could not open %s\n", outfilename);
	}
    }
    else /* user needs help */
    {
	printf("usage: %s framefile.osc [outfile.rgb] [-zoom factor] [-box x1 y1  x2 y2] [-negate]\n", argc[0]);
		
	printf("\n\t framefile.osc - the Raxis-IIc file you want to convert\n");
	printf(  "\t   outfile.rgb - name of output SGI RGB file.\n");
	printf(  "\t        factor - zoom in by this factor.\n");
	printf(  "\t  x1 y1  x2 y2 - corners of box to convert (original image pixel coordinates).\n");
	printf(  "\t       -negate - black spots on white background\n");
	printf("\n");
	printf(  "NOTE:\t DENZO's (X,Y) detector coordinate in *.x files is (Y,X) here.");
	printf("\n");
		
	/* report unhappy ending */
	file_type = UNKNOWN;
	return_code = 1;
    }

/* return appropriate exit status */
return return_code;
}

FILE *GetFrame(char *filename)
{
    FILE *frame;
    unsigned char string[1024];
    
    frame = fopen(filename, "rb");

    if(frame != NULL)
    {
	fread(header, 1, 2048, frame);

	/* What kind of file is this? */
	GetString(string, 0, 6);
		
	if(0!=strcmp(string, "R-AXIS"))
	{
	    /* probably not an R-AXIS frame */
	    file_type = UNKNOWN;
		
	    /* inform the user */
	    printf("ERROR: %s does not look like an R-AXIS frame!\n", filename);
	    /* skip this file */
	    fclose(frame);
	    
	    frame = NULL;
	}
	else
	{
	    GetString(string, 812, 4);
	    if(0==strcmp(string, "IRIS"))
	    {
		/* this is a "new" Raxis file */
		file_type = UNIX;
	    }
	    else
	    {
		/* probably from "old" Raxis */
		file_type = VMS;
	    }
	}
    }
    else
    {
	/* fopen() failed */
	perror("osc2sgi");
			
	file_type = UNKNOWN;
    }
    
    return frame;
}


char *GetString(char *buffer, int position, int length)
{
    memcpy(buffer, &header[position], length);
    buffer[length] = 0;
    nospaces(buffer);

    return buffer;
}

float GetFloat(position)
{
    FOURBYTES datum;
    
    memcpy(&datum, &header[position], 4);
    if(file_type == VMS) datum = fix_float(datum);

    return datum.real;
}

int GetInt(position)
{
    FOURBYTES datum;
    
    memcpy(&datum, &header[position], 4);
    if(file_type == VMS) datum = fix_int(datum);

    return datum.integer;
}


/* the following stuff used to be in swapper.c */
FOURBYTES fix_int(FOURBYTES wrong_order)
{
        FOURBYTES right_order;

        /* reorganize byte order */
        right_order.string[0] = wrong_order.string[3];
        right_order.string[1] = wrong_order.string[2];
        right_order.string[2] = wrong_order.string[1];
        right_order.string[3] = wrong_order.string[0];

        return right_order;
}

FOURBYTES fix_float(FOURBYTES wrong_float)
{
        FOURBYTES right_float;

        right_float.string[0] = wrong_float.string[1];
        right_float.string[1] = wrong_float.string[0];
        right_float.string[2] = wrong_float.string[3];
        right_float.string[3] = wrong_float.string[2];

        if(right_float.string[0] != 0) right_float.string[0] -= 1;

        return right_float;
}

void fix_int_list(FOURBYTES *wrong_order, int howfar)
{
        int i;

        for (i=0; i< howfar; ++i)
        {
                /* reorganize byte order */
                wrong_order[i]=fix_int(wrong_order[i]);
        }

        return;
}

void fix_float_list(FOURBYTES *wrong_order, int howfar)
{
        int i;

        for (i=0; i< howfar; ++i)
        {
                /* reorganize byte order */
                wrong_order[i]=fix_float(wrong_order[i]);
        }

        return;
}

void nospaces(char *string)
{
        /* start at the end of the string */
        int index = strlen(string)-1;

        /* look for the first non-space */
        while(string[index] == ' ')
        {
                --index;
        }
        /* truncate the string */
        string[index+1] = 0;

        return;
}

