/***********************************************************************
 * Common Tools -- Common classes used in various projects
 *
 * Copyright (C) 2002 Cyrius
 *
 * 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 (see the file COPYING); if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * or visit http://www.gnu.org/copyleft/gpl.html
 *
 ***********************************************************************
 * InputFile.cpp
 *
 *
 */

#include "OGMInputFile.h"
#include "../error.h"
#include <fcntl.h>

/*
 * Constructor
 */
OGMInputFile::OGMInputFile(void) {
	_initialize();
}

/*
 * Initialize values
 */
void OGMInputFile::_initialize(void) {
	//filename = NULL;
	file = -1;
	//filesize = -1;
	buffer = NULL;
	buffersize = -1;
	bufferpos = buffer;
	data_in_buffer = -1;
}

/*
 * Constructor
 */
OGMInputFile::OGMInputFile(char *name, long buffer_size)
: InputStream(name, -1) {
	_initialize();
	if(!name)
		throw MyError("OGMInputFile : No input name specified");

	file = OPEN(name, O_BINARY | O_RDONLY | O_SEQUENTIAL);
	// Open the file
	if(file == -1)
		throw MyError("Could not open the file [%s]\n", name);

	// Go to the end of the file
	if(LSEEKI64(file, 0, SEEK_END) == -1)
		throw MyError("Could not seek to the end of file [%s]\n", name);

	// Get the position of the end ( = filesize)
	size = TELLI64(file);
	if(LSEEKI64(file, 0, SEEK_SET) == -1)
		throw MyError("Could not seek to the beginning of file [%s]\n", name);

	buffer = (char *)malloc(buffer_size);
	if(!buffer)
		throw MyMemoryError();

	buffersize = buffer_size;
	bufferpos = buffer;
	data_in_buffer = 0;
}

/*
 * Destructor
 */
OGMInputFile::~OGMInputFile(void) {
#ifdef _DEBUG
	if(!_EOF())
		_RPT1(_CRT_WARN, "InputFile handling file [%s] closed before EOF reached\n", name);
#endif
	if(buffer)
		free(buffer);
	Close();
}

/*
 * Close the input
 */
void OGMInputFile::Close(void) {
	if(file != -1)
		CLOSE(file);
	file = -1;
}

/*
 * Seeking
 */
s64 OGMInputFile::Seek(s64 offset, int pos) {
	// Seek
	// Current position in file (pos n = byte n-1)
	s64 file_pos = TELLI64(file);
	s64 new_pos = 0;
	switch(pos) {
		case SEEK_SET:
			// Seeking from the beginning of the file
			new_pos = offset;
			break;
		case SEEK_CUR:
			// Seeking from current position
			new_pos = (file_pos - data_in_buffer) + offset;
			break;
		case SEEK_END:
			// Seeking from the end of the file
			new_pos = (get_size() - 1) + offset;
			break;
	}

	if( (new_pos >= file_pos - (data_in_buffer + (bufferpos-buffer)))
		&& (new_pos < file_pos) ) {
		// We remain in the buffer
		bufferpos = buffer + new_pos - (file_pos - (data_in_buffer + (bufferpos-buffer)));
		data_in_buffer = file_pos - new_pos;
		return new_pos;
	} else {
		// We seek outside the buffer, reset it and really seek
		bufferpos = buffer;
		data_in_buffer = 0;
	  return LSEEKI64(file, new_pos, SEEK_SET);
	}
}

/*
 * Tell if this is the end of the stream (file)
 */
bool OGMInputFile::_EOF(void) {
	if(E_O_F(file) && (data_in_buffer==0))
		return true;
	else
		return false;
}

/*
 * Write n_bytes bytes coming from the input to dest
 */
long OGMInputFile::Read(void *dest, long n_bytes) {
	long n_read = 0;

	if(n_bytes <= data_in_buffer) {
		// We have enough data in the buffer
		memcpy(dest, bufferpos, n_bytes);
		data_in_buffer -= n_bytes;
		bufferpos += n_bytes;
		n_read = n_bytes;
	} else {
		// Not enough data
		// Copy the rest to dest
		if(data_in_buffer) {
			memcpy(dest, bufferpos, data_in_buffer);
			n_read = data_in_buffer;
			data_in_buffer = 0;
		}
		// No need to bufferize the input, just give the data to dest
		int read = READ(file, (char *)dest+n_read, n_bytes-n_read);
		if(read>=0)
			n_read += read;
	}

	if(!data_in_buffer) {
		// No more data, prepare ourself to the next read
		if(!_EOF()) {
			bufferpos = buffer;
			data_in_buffer = READ(file, buffer, buffersize);
		}
	}

	return n_read;
}

/*
 * Will try to synch the stream on a pattern (within a maximum search length)
 */
bool OGMInputFile::SynchOn(unsigned short synch, unsigned short synch_bits, long max_search, long *search_length) {
	synch = SWAP_SHORT_ENDIAN(synch&synch_bits);
	synch_bits = SWAP_SHORT_ENDIAN(synch_bits);
	s64 position = get_pos();
	long search = 0;
	while(true) {
		if(search>max_search) {
			LSEEKI64(file, position, SEEK_SET);
			if(search_length)
				*search_length = search;
			return false;
		}
		if(data_in_buffer>=2) {
			// Enough data to test
			if(((*((unsigned short *)bufferpos))&synch_bits)==synch) {
				if(search_length)
					*search_length = search;
				return true;
			}
			bufferpos++;
			search++;
			data_in_buffer--;
		} else if(data_in_buffer==1) {
			if(E_O_F(file)) {
				// This is EOF, we haven't found
				if(search_length)
					*search_length = search;
				return true;
			}
			// Only 1 byte in the buffer
			// so just rewind 1 bytes and fill the buffer
			if(LSEEKI64(file, -1, SEEK_CUR)==-1)
				return false;
			bufferpos = buffer;
			data_in_buffer = READ(file, buffer, buffersize);
		} else {
			// No more bytes in buffer
			// Is this EOF ?
			if(_EOF()) {
				if(search_length)
					*search_length = search;
				return false;
			}
			// So fill the buffer
			bufferpos = buffer;
			data_in_buffer = READ(file, buffer, buffersize);
		}
	}
}