#include "TIFF_reader.h"

namespace MobilePhotoTool{


	TIFF_reader::TIFF_reader(MPT_Exception *exception, const char *path)
	{
		this->ifd			= NULL;
		this->number_ifd	= 0;

		buffer = new MPT_Buffer(exception, path);
		MPT_EXCEPTION_TEST;
		if(buffer->get_size(exception) < 8){
			//throw new MPT_Exception("The file can not be a TIFF or DNG file",__FILE__, __LINE__);
			MPT_EXCEPTION("The file can not be a TIFF or DNG file");
		}
		MPT_EXCEPTION_TEST;
		this->ifd_preview	= NULL;
		this->raw_processor	= NULL;

		this->exception = exception;
		if(this->exception == NULL){
			this->exception = exception = new MPT_Exception();
		}

		read_Header(exception);
	}


	TIFF_reader::~TIFF_reader(void)
	{ 
		size_t pos;
		if(this->ifd != NULL){
			for(pos = 0; pos < this->number_ifd; pos += 1){
				if(this->ifd[pos] != NULL){
					//this->ifd[pos]->~IFD();
					delete this->ifd[pos];
				}
			}
			//delete []this->ifd;
			free(this->ifd);
		}

		if(this->exception != NULL){
			delete exception;
			this->exception = NULL;
		}

		delete buffer;
		buffer = NULL;


		Raw_Processor_Creater::delete_Processor(this->raw_processor);
	}

	void TIFF_reader::read_Header(MPT_Exception *exception)
	{
		streamoff offset;
		bool readmore = true;
		IFD *ifd;

		switch(buffer->get_ushort(exception)){
		case 0x4949:
			buffer->set_order(exception, MPT_Buffer::LittleEndian);
			break;
		case 0x4D4D:
			buffer->set_order(exception, MPT_Buffer::BigEndian);
			break;
		}
		MPT_EXCEPTION_TEST;

		if(buffer->get_ushort(exception) == 42){
			offset = buffer->get_uint(exception);
			MPT_EXCEPTION_TEST;
			
			if(readmore){
				ifd = new IFD(exception, buffer, offset);
				if(ifd == NULL){
					MPT_EXCEPTION("No Memory");
				}
				MPT_EXCEPTION_TEST;

				offset = ifd->read(exception);
				MPT_EXCEPTION_TEST;
				this->set_IFD(exception, ifd);
				MPT_EXCEPTION_TEST;

				if(offset == 0){
					readmore = false;
				}
			}
		}else{
			MPT_EXCEPTION("The file can not be a TIFF or DNG file");
		}
	}

	void TIFF_reader::set_ifd_preview(MPT_Exception *exception)
	{
		if(ifd_preview == NULL){
			IFD *ifd;
			size_t pos;
			for(pos = 0; pos < this->number_ifd; pos++){
				ifd = this->ifd[pos];
				ifd = ifd->reduced_resolution_IFD(exception);
				MPT_EXCEPTION_TEST;
				if(ifd != NULL){
					ifd_preview = ifd;
				}
			}
		}
		if(ifd_preview == NULL){
			MPT_EXCEPTION("No preview image found.");
		}
	}

	unsigned int TIFF_reader::get_preview_width(MPT_Exception *exception)
	{
		unsigned int width = 0;
		set_ifd_preview(exception);
		MPT_EXCEPTION_TEST 0;

		width = ifd_preview->getImageWidth(exception);
		MPT_EXCEPTION_TEST 0;

		return width;
	}

	unsigned int TIFF_reader::get_preview_length(MPT_Exception *exception)
	{
		unsigned int length = 0;
		set_ifd_preview(exception);
		MPT_EXCEPTION_TEST 0;

		length = ifd_preview->getImageLength(exception);
		MPT_EXCEPTION_TEST 0;

		return length;
	}

	unsigned int TIFF_reader::getPreview_int_RGB(MPT_Exception *exception, unsigned int *data,  size_t size)
	{
		unsigned int	width, length, state, compression, *bitspersample, 
						photometricInterpretationtom, *stripoffsets, rowsperstrip;
		size_t			numberSamples, numberStrips, stripbytecount;
		IFD *ifd;
		state = 0;
		unsigned char in_data[3];

		bitspersample = stripoffsets = NULL;

		set_ifd_preview(exception);
		MPT_EXCEPTION_TEST 99;
		ifd = this->ifd_preview;
		width	= ifd->getImageWidth(exception);
		MPT_EXCEPTION_TEST 0;
		length	= ifd->getImageLength(exception);
		MPT_EXCEPTION_TEST 0;
		
		if(test_width_length(exception, width, length)){
			if(size >= width * length){
				compression 					= ifd->getCompression(exception);
				bitspersample 					= ifd->getBitsPerSample(exception, &numberSamples);
				photometricInterpretationtom	= ifd->getPhotometricInterpretation(exception);
				MPT_EXCEPTION_TEST 0;
				if(numberSamples == 3){
					if(	compression == 1 &&
						photometricInterpretationtom == 2 &&
						bitspersample[0] == 8 &&
						bitspersample[1] == 8 &&
						bitspersample[2] == 8 ){

							stripoffsets	= ifd->getStripOffsets(exception, &numberStrips);
							rowsperstrip	= ifd->getRowsPerStrip(exception);
							stripbytecount	= ifd->getStripByteCounts(exception);
							MPT_EXCEPTION_TEST 0;

							for(unsigned int strip = 0; strip < numberStrips; strip += 1){
								unsigned int startrow	= strip * rowsperstrip;
								unsigned int endrow		= startrow + rowsperstrip;
								unsigned int dest_row, pixel;
								if(endrow > length){
									endrow = length;
								}
								buffer->position(exception, stripoffsets[strip]);
								MPT_EXCEPTION_TEST 0;
								for(;startrow < endrow; startrow += 1){
									dest_row = startrow * width;
									for(unsigned int col = 0; col < width; col += 1){
										buffer->get_ubytes(exception, in_data, 3);
										MPT_EXCEPTION_TEST 0;
										pixel = in_data[0];
										pixel <<= 8;
										pixel |= in_data[1];
										pixel <<= 8;
										pixel |= in_data[2];
										data[dest_row + col] = pixel;
									}
								}
							}	

					}else{
						state = 4;
					}
				}else{
					state = 3;
				}
			}else{
				state = 2;
			}
		}else{
			state = 1;
		}

		if(bitspersample != NULL){
			delete []bitspersample;
		}
		
		if(stripoffsets != NULL){
			delete []stripoffsets;
		}
		return state;
	}

	bool TIFF_reader::test_width_length(MPT_Exception *, unsigned int width, unsigned int length)
	{
		bool back = false;
		if((TIFF_reader::MAX_PIXEL / width) > length){
			back = true;
		}

		return back;
	}

	void TIFF_reader::load_Raw_Processor(MPT_Exception *exception)
	{
		IFD *raw_ifd = NULL;
		
		size_t pos;
		for(pos = 0; pos < this->number_ifd; pos++){
			raw_ifd = this->ifd[pos];
			raw_ifd = raw_ifd->raw_IFD(exception);
			MPT_EXCEPTION_TEST;
			if(raw_ifd != NULL){
				break;
			}
		}
		if(raw_ifd == NULL){
			MPT_EXCEPTION("No raw image found.");
		}

		this->raw_processor = Raw_Processor_Creater::create_Processor(exception, raw_ifd, this->ifd[0]);
		MPT_EXCEPTION_TEST;

	}

	Raw_Processor *TIFF_reader::get_Raw_Processor(MPT_Exception *exception)
	{
		if(this->raw_processor == NULL){
			this->load_Raw_Processor(exception);
			MPT_EXCEPTION_TEST NULL;
		}
		return this->raw_processor;
	}

	MPT_Exception *TIFF_reader::get_exception()
	{
		MPT_Exception *exception = this->exception;

		if(exception == NULL){
			this->exception = exception = new MPT_Exception();
		}
		return exception;
	}

	void TIFF_reader::set_IFD(MPT_Exception *exception, IFD *ifd)
	{
		size_t temp;
		if(this->ifd == NULL){
			this->ifd = (IFD**)malloc(sizeof(IFD *));
			if(this->ifd == NULL){
				MPT_EXCEPTION("Out of memory.");
			}
			this->number_ifd = 1;
			this->ifd[0] = ifd;
		}else{
			temp = this->number_ifd + 1;
			this->ifd = (IFD**)realloc(this->ifd, sizeof(IFD *) * temp);
			if(this->ifd == NULL){
				MPT_EXCEPTION("Out of memory.");
			}
			this->ifd[this->number_ifd] = ifd;
			this->number_ifd += 1;
		}
	}
}
