/*******************************************************************

            MPEG file I/O interface (very very very slow)

 *******************************************************************/

#include <io.h>
#include <fcntl.h>

#define MPEG_IO_C
#include "mpeg_io.h"

int open_mpeg_file(char *path, MPEG_IO *out);
int close_mpeg_file(MPEG_IO *p);
int get_bits(MPEG_IO *in, int num_of_bits);
int read_bits(MPEG_IO *in, int num_of_bits);
int erase_bits(MPEG_IO *in, int num_of_bits);
int next_marker(MPEG_IO *in);

static int fill_bits(MPEG_IO *p);
static int mpeg_io_getc(MPEG_IO *p);
/*******************************************************************/
int open_mpeg_file(char *path, MPEG_IO *out)
{

	memset(out, 0, sizeof(MPEG_IO));
	
	strcpy(out->path, path);

	out->fd = _open(path, _O_BINARY|_O_RDONLY);
	if(out->fd == -1){
		return 0;
	}

	out->buffer_size = _read(out->fd, out->buffer+2, MPEG_IO_BUFFER_SIZE);
	if(out->buffer_size == 0){
		return 0;
	}

	out->current = out->buffer+2;
	
	fill_bits(out);

	return 1;
}
/*******************************************************************/
int close_mpeg_file(MPEG_IO *p)
{
	_close(p->fd);
	return 1;
}
/*******************************************************************/
int get_bits(MPEG_IO *in, int num_of_bits)
{
	int r,n;

	if(num_of_bits > in->bits_rest){
		n = num_of_bits - in->bits_rest;
		r = in->bits << n;
		
		in->bits_rest = 0;
		in->bits = 0;
		fill_bits(in);

		r |= get_bits(in, n);
	}else{
		n = in->bits_rest - num_of_bits;
		r = in->bits >> n;
		in->bits &= (1<<n)-1;
		in->bits_rest = n;
		fill_bits(in);
	}

	return r;
}
/*******************************************************************/
int read_bits(MPEG_IO *in, int num_of_bits)
{

	if(!fill_bits(in)){
		return 0;
	}
	
	if(num_of_bits > in->bits_rest){
		return 0;
	}

	return in->bits >> (in->bits_rest - num_of_bits);
}
/*******************************************************************/
int erase_bits(MPEG_IO *in, int num_of_bits)
{
	int n;

	if(!num_of_bits){
		return 1;
	}

	n = in->bits_rest - num_of_bits;
	if(n < 0){
		return 0;
	}

	in->bits &= (1<<n)-1;
	in->bits_rest = n;

	return fill_bits(in);
}
/*******************************************************************/
int next_marker(MPEG_IO *in)
{
	int i;

	static const unsigned char p[3] = {
		0, 0, 1,
	};
	
	static const int jump_table[256] = {
		1, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,

		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,

		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,

		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,

		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,

		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,

		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,

		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3,
	};
	
	erase_bits(in, in->bits_rest%8);

	if(in->bits >> 8 == 1){
		return 1;
	}else if(in->bits & 0xffffff == 1){
		erase_bits(in, 8);
		return 1;
	}

	if(in->current < in->buffer+2){
		in->current[-2] = (in->bits >> 8) & 0xff;
		in->current[-1] = in->bits & 0xff;
	}

	i = 3;
	
	while(i != 0){
		if(in->current > in->buffer + in->buffer_size + 2){
			memcpy(in->buffer, in->buffer + in->buffer_size, 2);
			in->buffer_size = _read(in->fd, in->buffer+2, MPEG_IO_BUFFER_SIZE);
			in->current = in->buffer+2;
			i = 3;
			if(in->buffer_size == 0){
				return 0;
			}
		}
		if(*(in->current) == p[i-1]){
			i--;
			in->current--;
		}else{
			i = 3;
			in->current += jump_table[*(in->current)];
		}
	}

	in->current += 1;

	erase_bits(in, 32);
	
	return 1;
}
/*-----------------------------------------------------------------*/
static int fill_bits(MPEG_IO *p)
{
	int i,n,c;

	n = sizeof(int)*8 - p->bits_rest;
	n /= 8;
	
	for(i=0;i<n;i++){
		c = mpeg_io_getc(p);
		if(c == EOF){
			return 0;
		}
		p->bits <<= 8;
		p->bits |= c;
		p->bits_rest += 8;
	}

	return 1;
}
/*-----------------------------------------------------------------*/
static int mpeg_io_getc(MPEG_IO *p)
{
	int r;
	
	if(p->current > p->buffer + p->buffer_size +2){
		p->buffer_size = _read(p->fd, p->buffer+2, MPEG_IO_BUFFER_SIZE);
		p->current = p->buffer+2;
		if(p->buffer_size == 0){
			return EOF;
		}
	}

	r = *(p->current);
	p->current += 1;
	
	return r;
}