// TIFFCONV.CPP

typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned long ulong;

#include <iostream>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "..\scanfile.h"

using namespace std; //introduces namespace std

/* Function Prototypes */

void convert(int type);
short rotate_short(short inbuf);
unsigned long rotate_ulong(unsigned long inbuf);
void file_close(void);

/* Definitions */

#define	TIFF				1
#define T_ASCII				2
#define T_SHORT				3
#define T_LONG				4
#define T_RATIONAL			5
#define DIR_ENTRIES			15
#define MAX_IMAGE_WIDTH		250		// Columns
#define MAX_IMAGE_LENGTH	400		// Rows

/* Variable Declarations */

FILE *out, *in;

/*************************************************************/

int main ( void )
{
	char menu_key;
		
	while(1)
  	{

		cout << "\n\n\nScanner File Converter 1.0";
		cout << "\n1 - Convert scan file to TIFF.";
		cout << "\n9 - Exit.";
    	cout << "\n\nEnter Selection: ";

    	cin >> menu_key;

    	switch(menu_key)
    	{
      		case '1':
				convert(TIFF);
				break;


      		case '9':
				exit(0);
    	}
  	}
}

/*******************************************************/

void convert(int type)
{
#pragma unused(type)

  	char file_in[80], file_out[80];
	int file_num;
	ulong image_width, col, image_length, row;
	ulong image_byte_count, x_resolution_offset, y_resolution_offset;
	ulong next_ifd = 0;
	ulong data_offset;
	ushort ifd_entries = DIR_ENTRIES;
	ushort ping_buff[END_BIN + 1], z_elev;
	uchar scan_line[MAX_IMAGE_WIDTH];
			
	struct
	{
		ushort byte_order;
		ushort tiff_type;
		ulong ifd_offset;
	} header;

	ulong x_resolution[2];
	ulong y_resolution[2];
	
	struct dir_entry
	{
		ushort tag;
		ushort val_type;
		ulong count;
		union
		{
			ushort vshort[2];
			ulong vlong;			
		} value;
	};
	
	struct dir_entry ifd[DIR_ENTRIES];
	struct def scan_info;
	

	// Get the scanner system info
	if((in = fopen("scaninfo.dat", "rb")) == NULL)
	{
		cout << "\nCould not open scaninfo.dat";
		file_close();
		return;
	}

  	cout << sizeof(scan_info);
  	
  	if( fread(&scan_info, sizeof(scan_info), 1, in) != 1 )
  	{
    	cout << "\nError reading scaninfo.bin";
    	file_close();
    	return;
  	}

  	if(fclose(in))
  	{
  		cout << "\nScaninfo.bin file close error";
		file_close();
		return;
	}
	

	file_num = 0;

  	// File conversion loop
	while(1)
	{
	   
	    sprintf(file_in, "%d.bin", file_num);  // Make a name for the output file

	  	// Open the input file
		if((in = fopen(file_in, "rb")) == NULL)
		{
			cout << "\nCould not open " << file_in;
			file_close();
			return;
		}
	  
	 
	  	// Open the output file
	    sprintf(file_out, "%d.tif", file_num);  // Make a name for the output file

		if((out = fopen(file_out, "wb")) == NULL)
	 	{
	 		cout << "\nCould not open " << file_out;
	 		file_close();
	 		return;
	 	}


	 	cout << "\nWriting TIFF file " << file_num;
		file_num++;	  
	
	  	// Figure the image data
		header.byte_order = 0x4949;
		header.tiff_type = 42;

	  	image_width = MAX_IMAGE_WIDTH;

		image_length = FLUOR_CELL_SIZE * rotate_short(scan_info.y_scan_len);
		if(image_length >= MAX_IMAGE_LENGTH)
		{
			cout << "\nImage length to large: " << image_length;
			return;
		}

		image_byte_count = image_width * image_length;

	  	x_resolution[0] = 10;
	  	x_resolution[1] = 0;
	  	y_resolution[0] = 10;
	  	y_resolution[1] = 0;

		x_resolution_offset = 8;
		y_resolution_offset = x_resolution_offset + sizeof(x_resolution);
		data_offset = y_resolution_offset + sizeof(y_resolution);
		header.ifd_offset = data_offset + image_byte_count;

	
		// New subfile
		ifd[0].tag = 254;
		ifd[0].val_type = T_LONG;
		ifd[0].count = 1;
		ifd[0].value.vlong = 0;
		
		// Image width
		ifd[1].tag = 256;
		ifd[1].val_type = T_LONG;
		ifd[1].count = 1;
		ifd[1].value.vlong = image_width;
		
		// Image length
		ifd[2].tag = 257;
		ifd[2].val_type = T_LONG;
		ifd[2].count = 1;
		ifd[2].value.vlong = image_length;
		
		// Image bits per sample
		ifd[3].tag = 258;
		ifd[3].val_type = T_SHORT;
		ifd[3].count = 1;
		ifd[3].value.vshort[0] = 8;
		ifd[3].value.vshort[1] = 0;
	
		// Image compression
		ifd[4].tag = 259;
		ifd[4].val_type = T_SHORT;
		ifd[4].count = 1;
		ifd[4].value.vshort[0] = 1;
		ifd[4].value.vshort[1] = 0;
	
		// Image photometric interpretation
		ifd[5].tag = 262;
		ifd[5].val_type = T_SHORT;
		ifd[5].count = 1;
		ifd[5].value.vshort[0] = 0;
		ifd[5].value.vshort[1] = 0;
	
		// Image description
		ifd[6].tag = 270;
		ifd[6].val_type = T_ASCII;
		ifd[6].count = 1;
		ifd[6].value.vlong = 8;
		
		// Image strip offsets
		ifd[7].tag = 273;
		ifd[7].val_type = T_LONG;
		ifd[7].count = 1;
		ifd[7].value.vlong = data_offset;
	
		// Image orientation
		ifd[8].tag = 274;
		ifd[8].val_type = T_SHORT;
		ifd[8].count = 1;
		ifd[8].value.vshort[0] = 1;
		ifd[8].value.vshort[1] = 0;
	
		// Image samples per pixel
		ifd[9].tag = 277;
		ifd[9].val_type = T_SHORT;
		ifd[9].count = 1;
		ifd[9].value.vshort[0] = 1;
		ifd[9].value.vshort[1] = 0;
	
		// Image rows per strip
		ifd[10].tag = 278;
		ifd[10].val_type = T_LONG;
		ifd[10].count = 1;
		ifd[10].value.vlong = image_length;
	
		// Image strip byte counts
		ifd[11].tag = 279;
		ifd[11].val_type = T_LONG;
		ifd[11].count = 1;
		ifd[11].value.vlong = image_byte_count;
	
		// Image X resolution
		ifd[12].tag = 282;
		ifd[12].val_type = T_RATIONAL;
		ifd[12].count = 1;
		ifd[12].value.vlong = x_resolution_offset;
	
		// Image Y resolution
		ifd[13].tag = 283;
		ifd[13].val_type = T_RATIONAL;
		ifd[13].count = 1;
		ifd[13].value.vlong = y_resolution_offset;
	
		// Image resolution unit
		ifd[14].tag = 296;
		ifd[14].val_type = T_SHORT;
		ifd[14].count = 1;
		ifd[14].value.vshort[0] = 1;
		ifd[14].value.vshort[1] = 0;
	
	   
	  	
		// Write the header
	    fwrite(&header, sizeof(header), 1, out); 
	    
	   	// Write the IFD values
	    fwrite(x_resolution, sizeof(x_resolution), 1, out); 
	    fwrite(y_resolution, sizeof(y_resolution), 1, out); 
	    
		// Write the image data
		for(row=0; row< image_length; row++)
		{
		  	if( fread(ping_buff, sizeof(ushort),END_BIN+1, in) != END_BIN+1 )
		  	{
		    	cout << "\nError reading " << file_in;
		    	cout << "\nrow = " << row;
		    	file_close();
		    	return;
		  	}
				
			for(col=0; col< MAX_IMAGE_WIDTH; col++)		// Initialize the scan line buffer
			{
				scan_line[col] = 0;
			}

			z_elev = rotate_short(ping_buff[0]);
			cout << "\nz_elev = " << z_elev;
			if(END_BIN+z_elev > MAX_IMAGE_WIDTH)
			{
				cout << "\nError: z_elev out of bounds " << z_elev << "row " << row;
		    	file_close();
		    	return;
			}
			
			for(col=1; col<= END_BIN; col++)
			{
				scan_line[col+z_elev] = (uchar)(40 * log10(rotate_short(ping_buff[col])));
			}
			
			if(fwrite(scan_line, sizeof(char), MAX_IMAGE_WIDTH, out) != MAX_IMAGE_WIDTH)
		  	{
		    	cout << "\nError writing to " << file_out;
		    	cout << "\nrow = " << row;
		    	file_close();
		    	return;
			} 
		}
		
		// Write the number of IFD entries
	    fwrite(&ifd_entries, sizeof(ifd_entries), 1, out); 
	
		// Write the IFD
	    fwrite(&ifd, sizeof(ifd), 1, out); 
	    
		// Write the next IFD address
	    fwrite(&next_ifd, sizeof(next_ifd), 1, out); 
	    
		fclose(in);
		fclose(out);
	}
  	
  	file_close();
  	return;
}


/************************************************************
  The following rotate functions are used to correct the
  difference between the way Motorola and Intel processors
  store their data.  Motorola stores the high-byte of words
  in lower addresses.  Intel stores the high_byte in higher
  addresses.  Files transferred with XMODEM need to be corrected.
**************************************************************/

short rotate_short(short inbuf){
  union ushort_buf{
    unsigned short word;
    unsigned char byte[2];
  } tempbuf1, tempbuf2;

  tempbuf1.word = inbuf;
  tempbuf2.byte[0] = tempbuf1.byte[1];
  tempbuf2.byte[1] = tempbuf1.byte[0];

  return tempbuf2.word;
}

/**************************************************/


unsigned long rotate_ulong(unsigned long inbuf){
  union ulong_buf{
    unsigned long word;
    unsigned char byte[4];
  } tempbuf1, tempbuf2;

  tempbuf1.word = inbuf;
  tempbuf2.byte[0] = tempbuf1.byte[3];
  tempbuf2.byte[1] = tempbuf1.byte[2];
  tempbuf2.byte[2] = tempbuf1.byte[1];
  tempbuf2.byte[3] = tempbuf1.byte[0];

  return tempbuf2.word;
}

/*****************************************************/


void file_close(void)
{
	fclose(in);
	fclose(out);
}

/*****************************************************/
