#include "MPT_Buffer.h"

namespace MobilePhotoTool{

	MPT_Buffer::MPT_Buffer(MPT_Exception *exception, const char *path)
	{
		file =  fopen(path, "rb");
		if(file == NULL){
			MPT_EXCEPTION("Unable to open the file") ;
		}
		fseek(file, 0, SEEK_END),
		size = ftell(file);
		fseek(file, 0, SEEK_SET);

		mutex = (MPT_THREAD_MUTEX_T*)malloc(sizeof(MPT_THREAD_MUTEX_T));
		if(mutex == NULL){
			MPT_EXCEPTION("Out of Memory");
		}
		if(MPT_THREAD_MUTEX_INIT(mutex, NULL) != 0){
			MPT_EXCEPTION("Thread Exception");
		}

		order = BigEndian;
	}


	MPT_Buffer::~MPT_Buffer(void)
	{
		fclose(file);

		if(mutex != NULL){
			MPT_THREAD_MUTEX_DESTROY(mutex);
			free(mutex);
			mutex = NULL;
		}
	}

	size_t MPT_Buffer::position(MPT_Exception *exception)
	{
		long position = 0;
		if(!ferror(file)){
			position = ftell(file);
		}else{
			MPT_EXCEPTION("Unable to read the file.") 0;
		}
		return position;
	}

	void MPT_Buffer::position(MPT_Exception *exception, const size_t position)
	{
		if(position < size){
			if(!ferror(file)){
				fseek(file, (long)position, SEEK_SET);
			}else{
				MPT_EXCEPTION("Unable to read the file.");
			}
		}else{
			MPT_EXCEPTION("The position is out of range.");
		}
	}

	void MPT_Buffer::set_order(MPT_Exception *, Endian endian)
	{
		order = endian;
	}

	unsigned short MPT_Buffer::get_ushort(MPT_Exception *exception)
	{
		unsigned short value = 0;
		unsigned char input[2];
		//long pos = ftell(file);
		if(!fread((char *)input, 2, 1, file)){
			MPT_EXCEPTION("Can not read the file eof") 0;
		}
		if(order == BigEndian){
			value = input[0] << 8;
			value |= input[1];
		}else{
			value = input[1] << 8;
			value |= input[0];
		}

		return value;
	}

	unsigned int MPT_Buffer::get_uint(MPT_Exception *exception)
	{
		unsigned int value = 0;
		unsigned char input[4];
		if(!fread((char *)input, 4, 1, file)){
			MPT_EXCEPTION("Can not read the file") 0;
		}
		if(order == BigEndian){
			value = input[0] << 24;
			value |= input[1] << 16;
			value |= input[2] << 8;
			value |= input[3];
		}else{
			value = input[3] << 24;
			value |= input[2] << 16;
			value |= input[1] << 8;
			value |= input[0];
		}

		return value;
	}

	int MPT_Buffer::get_int(MPT_Exception *exception)
	{
		int value = 0;
		unsigned char input[4];
		if(!fread((char *)input, 4, 1, file)){
			MPT_EXCEPTION("Can not read the file") 0;
		}
		if(order == BigEndian){
			value = input[0] << 24;
			value |= input[1] << 16;
			value |= input[2] << 8;
			value |= input[3];
		}else{
			value = input[3] << 24;
			value |= input[2] << 16;
			value |= input[1] << 8;
			value |= input[0];
		}

		return value;
	}

	unsigned char MPT_Buffer::get_ubyte(MPT_Exception *exception)
	{
		unsigned char value = 0;
		//unsigned char input[1];
		if(!fread((char*)&(value), 1, 1, file)){
			MPT_EXCEPTION("Can not read the file") 0;
		}
		
		return value;
	}

	size_t MPT_Buffer::get_size(MPT_Exception *)
	{
		return size;
	}

	size_t MPT_Buffer::get_ubytes(MPT_Exception *exception, unsigned char *data, size_t count)
	{
		size_t number = 0;
		number = fread((char *)data, 1, count, file);
		if(number != count){
			MPT_EXCEPTION("Can not read the file") 0;
		}
		return number;
	}

	size_t MPT_Buffer::get_bytes(MPT_Exception *exception, char *data, size_t count)
	{
		size_t number = 0;
		number = fread(data, 1, count, file);
		if(number != count){
			MPT_EXCEPTION("Can not read the file") 0;
		}
		return number;
	}

	size_t MPT_Buffer::get_ushort_ts(MPT_Exception *exception, unsigned short *value, unsigned long position)
	{
		unsigned short invalue = 0;
		unsigned char input[2];
		size_t num_bytes = 0;

		MPT_THREAD_MUTEX_LOCK(mutex);
		this->position(exception, position);
		if(exception->exception){
			MPT_THREAD_MUTEX_UNLOCK(mutex);
			return 0;
		}
		num_bytes = fread((char *)input, 2, 1, file);
		MPT_THREAD_MUTEX_UNLOCK(mutex);

		if(num_bytes != 2){
			MPT_EXCEPTION("Can not read the file eof") 0;
		}
		if(order == BigEndian){
			invalue = input[0] << 8;
			invalue |= input[1];
		}else{
			invalue = input[1] << 8;
			invalue |= input[0];
		}
		*value = invalue;

		return num_bytes;
	}

	size_t MPT_Buffer::get_uint_ts(MPT_Exception *exception, unsigned int *value, unsigned long position)
	{
		unsigned int invalue = 0;
		unsigned char input[4];
		size_t num_bytes = 0;

		MPT_THREAD_MUTEX_LOCK(mutex);
		this->position(exception, position);
		if(exception->exception){
			MPT_THREAD_MUTEX_UNLOCK(mutex);
			return 0;
		}
		num_bytes = fread((char *)input, 4, 1, file);
		MPT_THREAD_MUTEX_UNLOCK(mutex);

		if(num_bytes != 4){
			MPT_EXCEPTION("Can not read the file") 0;
		}

		if(order == BigEndian){
			invalue = input[0] << 24;
			invalue |= input[1] << 16;
			invalue |= input[2] << 8;
			invalue |= input[3];
		}else{
			invalue = input[3] << 24;
			invalue |= input[2] << 16;
			invalue |= input[1] << 8;
			invalue |= input[0];
		}

		*value = invalue;

		return num_bytes;
	}

	size_t MPT_Buffer::get_int_ts(MPT_Exception *exception, int *value, unsigned long position)
	{
		int invalue = 0;
		unsigned char input[4];
		size_t num_bytes = 0;

		MPT_THREAD_MUTEX_LOCK(mutex);
		this->position(exception, position);
		if(exception->exception){
			MPT_THREAD_MUTEX_UNLOCK(mutex);
			return 0;
		}
		num_bytes = fread((char *)input, 4, 1, file);
		MPT_THREAD_MUTEX_UNLOCK(mutex);

		if(num_bytes != 4){
			MPT_EXCEPTION("Can not read the file") 0;
		}

		if(order == BigEndian){
			invalue = input[0] << 24;
			invalue |= input[1] << 16;
			invalue |= input[2] << 8;
			invalue |= input[3];
		}else{
			invalue = input[3] << 24;
			invalue |= input[2] << 16;
			invalue |= input[1] << 8;
			invalue |= input[0];
		}

		*value = invalue;

		return num_bytes;
	}

	size_t MPT_Buffer::get_ubyte_ts(MPT_Exception *exception, unsigned char *value, unsigned long position)
	{
		size_t num_bytes = 0;

		MPT_THREAD_MUTEX_LOCK(mutex);
		this->position(exception, position);
		if(exception->exception){
			MPT_THREAD_MUTEX_UNLOCK(mutex);
			return 0;
		}
		num_bytes = fread((char *)value, 1, 1, file);
		MPT_THREAD_MUTEX_UNLOCK(mutex);

		if(num_bytes != 1){
			MPT_EXCEPTION("Can not read the file") 0;
		}

		return num_bytes;
	}

	size_t MPT_Buffer::get_ubytes_ts(MPT_Exception *exception, unsigned char *data, size_t count, unsigned long position)
	{
		size_t num_bytes = 0;

		MPT_THREAD_MUTEX_LOCK(mutex);
		this->position(exception, position);
		if(exception->exception){
			MPT_THREAD_MUTEX_UNLOCK(mutex);
			return 0;
		}
		num_bytes = fread((char *)data, 1, count, file);
		MPT_THREAD_MUTEX_UNLOCK(mutex);

		if(num_bytes != count){
			MPT_EXCEPTION("Can not read the file") 0;
		}

		return num_bytes;
	}

	size_t MPT_Buffer::get_bytes_ts(MPT_Exception *exception, char *data, size_t count, unsigned long position)
	{
		size_t num_bytes = 0;

		MPT_THREAD_MUTEX_LOCK(mutex);
		this->position(exception, position);
		if(exception->exception){
			MPT_THREAD_MUTEX_UNLOCK(mutex);
			return 0;
		}
		num_bytes = fread((char *)data, 1, count, file);
		MPT_THREAD_MUTEX_UNLOCK(mutex);

		if(num_bytes != count){
			MPT_EXCEPTION("Can not read the file") 0;
		}

		return num_bytes;
	}

}
