/*******************************************************************
                            GOP routine
 *******************************************************************/

#include <io.h>
#include "sequence_header.h"

#define GOP_C
#include "gop.h"

int next_gop(MPEG_IO *p);
int read_gop(GOP_TC *in, GOP *out);
int count_frame(GOP_TC *p);
GOP gop_tc_find_gop(void *p, int frame);
int last_gop(MPEG_IO *p);

/*******************************************************************/
int next_gop(MPEG_IO *p)
{
	unsigned int code;

	while(next_start_code(p)){
		code = get_bits(p, 32);
		if(code == 0x1B8){
			return 1;
		}
	}

	return 0;
}
/*******************************************************************/
int read_gop(GOP_TC *in, GOP *out)
{
	PICTURE_HEADER pic;
	TIMECODE t;
	unsigned int code;
	int intra_offset;
	int gop;
	int frame;
	int frame_rate;
	FRAME_PER_SCALE *fps;

	intra_offset = 0;
	
	fps = in->fps;
	frame_rate = fps->frame / fps->scale + ((fps->frame % fps->scale) ? 1 : 0 );

	gop = 0;
	if(! next_gop(in->p)){
		return 0;
	}

	out->offset = mpeg_io_tell(in->p) - 4;

	read_timecode(in->p, &t);
	out->frame = timecode2frame(&t, frame_rate);

	frame = 0;
	while(next_start_code(in->p)){
		code = get_bits(in->p, 32);
		if(code == 0x100){
			frame += 1;
			read_picture_header(in->p, &pic, in->pic_opt);
			if(pic.picture_coding_type != 1){
				continue;
			}
			if(gop){
				return frame - 1 - intra_offset + pic.temporal_reference;
			}
			intra_offset = pic.temporal_reference;
			
		}else if(code == 0x1B8){
			read_timecode(in->p, &t);
			if(out->frame + frame != timecode2frame(&t, frame_rate)){
				return 0;
			}
			out->frame += intra_offset;
			gop = 1;
		}
	}

	return frame - intra_offset;
}
/*******************************************************************/
int count_frame(GOP_TC *p)
{
	int start,last;
	GOP g;

	mpeg_io_seek(p->p, 0, SEEK_SET);

	if(! read_gop(p, &g)){
		return -1;
	}

	start = g.frame;

	last_gop(p->p);

	last = read_gop(p, &g);
	last += g.frame;

	return last - start;
}	
/*******************************************************************/
int last_gop(MPEG_IO *p)
{
	int i;

	static const unsigned char s[4] = {
		0, 0, 1, 0xB8,
	};
	
	static const int jump_table[256] = {
		1, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,

		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,

		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5,

		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
	};

	if(p->file_length < MPEG_IO_BUFFER_SIZE){
		lseek(p->fd, 0, SEEK_SET);
	}else{
		lseek(p->fd, - MPEG_IO_BUFFER_SIZE, SEEK_END);
	}

	p->buffer_size = read(p->fd, p->buffer, MPEG_IO_BUFFER_SIZE);
	p->file_position = tell(p->fd) + MPEG_IO_BUFFER_SPARE;
	p->current = p->buffer + p->buffer_size - 1;
	
	i = 0;
	while(i < 4){
		if(p->current - i < p->buffer){
			memcpy(p->buffer + MPEG_IO_BUFFER_SIZE, p->buffer, MPEG_IO_BUFFER_SPARE);
			lseek(p->fd, - 2 * MPEG_IO_BUFFER_SIZE, SEEK_CUR);
			p->buffer_size = read(p->fd, p->buffer, MPEG_IO_BUFFER_SIZE);
			p->current = p->buffer+MPEG_IO_BUFFER_SIZE+3;
			i = 0;
			p->file_position = tell(p->fd) + MPEG_IO_BUFFER_SPARE;
		}
		if(*(p->current-i) == s[3-i]){
			i++;
		}else{
			i = 0;
			p->current -= jump_table[*(p->current-4)];
		}
	}
	
	p->current -= 3;

	erase_bits(p, p->bits_rest);
	
	fprintf(stdout, "debug - offset = %d\n", mpeg_io_tell(p));

	return 1;
}	
/*******************************************************************/
GOP gop_tc_find_gop(void *p, int frame)
{
	size_t first,last, i;
	int n;

	GOP r;

	GOP_TC *mt;

	mt = (GOP_TC *)p;

	first = 0;
	last = mt->p->file_length;

	while(1){
		i = first + (last - first)/2;
		
		if(i == first){
			break;
		}
		
		mpeg_io_seek(mt->p, i, SEEK_SET);
		
		if((n =  read_gop(mt, &r)) == 0){
			last = i;
            continue;
		}
		
		if(r.frame > frame){
			last = i;
		}else if(r.frame + n > frame){
			return r;
		}else{
			first = r.offset;
		}
	}
		
	r.offset = 0;
	r.frame = -1;
	
	return r;		
}
