/*******************************************************************
                            GOP routine
 *******************************************************************/

#include <io.h>
#include "sequence_header.h"

#define GOP_C
#include "gop.h"

int next_gop(VIDEO_STREAM *p);
int last_gop(VIDEO_STREAM *p);

READ_GOP_PARAMETER *new_read_gop_parameter(VIDEO_STREAM *stream, SEQUENCE_HEADER *seq, READ_PICTURE_HEADER_OPTION *pic_opt);
void delete_read_gop_parameter(void *p);

int read_gop(READ_GOP_PARAMETER *in, GOP *out);
int count_frame(READ_GOP_PARAMETER *p);

GOP find_gop_with_timecode(void *p, int frame);


int next_gop(VIDEO_STREAM *p)
{
	unsigned int code;

	while(next_start_code(p)){
		code = get_bits(p, 32);
		if(code == 0x1B8){
			return 1;
		}
	}

	return 0;
}

int last_gop(VIDEO_STREAM *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 < VIDEO_STREAM_BUFFER_SIZE){
		lseek(p->fd, 0, SEEK_SET);
	}else{
		lseek(p->fd, - VIDEO_STREAM_BUFFER_SIZE, SEEK_END);
	}

	p->buffer_size = read(p->fd, p->buffer, VIDEO_STREAM_BUFFER_SIZE);
	p->file_position = tell(p->fd) + VIDEO_STREAM_BUFFER_SPARE;
	p->current = p->buffer + p->buffer_size - 1;
	
	i = 0;
	while(i < 4){
		if(p->current - i < p->buffer){
			memcpy(p->buffer + VIDEO_STREAM_BUFFER_SIZE, p->buffer, VIDEO_STREAM_BUFFER_SPARE);
			lseek(p->fd, - 2 * VIDEO_STREAM_BUFFER_SIZE, SEEK_CUR);
			p->buffer_size = read(p->fd, p->buffer, VIDEO_STREAM_BUFFER_SIZE);
			p->current = p->buffer+VIDEO_STREAM_BUFFER_SIZE+3;
			i = 0;
			p->file_position = tell(p->fd) + VIDEO_STREAM_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);
	
	return 1;
}	

READ_GOP_PARAMETER *new_read_gop_parameter(VIDEO_STREAM *stream, SEQUENCE_HEADER *seq, READ_PICTURE_HEADER_OPTION *pic_opt)
{
	READ_GOP_PARAMETER *r;

	static const int frame[16] = {
		 0, 23976, 24, 25, 2997, 30, 50, 5994,
		60,     0,  0,  0,    0,  0,  0,    0,
	};

	static const int scale[16] = {
		 1,  1000,  1,  1,  100,  1,  1,  100,
		 1,     1,  1,  1,    1,  1,  1,    1,
	};

	r = (READ_GOP_PARAMETER *)calloc(1, sizeof(READ_GOP_PARAMETER));
	if(r == NULL){
		return NULL;
	}

	r->p = stream;

	r->frame = frame[seq->picture_rate];
	r->scale = scale[seq->picture_rate];

	if(seq->has_sequence_extension){
		r->frame *= (seq->se.frame_rate_ext_n + 1);
		r->scale *= (seq->se.frame_rate_ext_d + 1);
	}

	r->pic_opt = pic_opt;

	return r;
}

void delete_read_gop_parameter(void *p)
{
	if(p != NULL){
		free(p);
	}
}	

int read_gop(READ_GOP_PARAMETER *in, GOP *out)
{
	PICTURE_HEADER pic;
	TIMECODE t;
	unsigned int code;
	int intra_offset;
	int gop;
	int frame;
	int frame_rate;

	intra_offset = 0;
	
	frame_rate = (in->frame + in->scale - 1 ) / in->scale;

	gop = 0;
	if(! next_gop(in->p)){
		return 0;
	}

	out->offset = video_stream_tell(in->p) - 4;

	read_timecode(in->p, &t);
	out->start_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){
				out->frame_count = frame - 1 - intra_offset + pic.temporal_reference;
				return out->frame_count;
			}
			intra_offset = pic.temporal_reference;
			out->start_frame += intra_offset;

		}else if(code == 0x1B8){
			read_timecode(in->p, &t);
			if(out->start_frame - intra_offset + frame != timecode2frame(&t, frame_rate)){
				return 0;
			}
			gop = 1;
		}
	}

	out->frame_count = frame - intra_offset;
	return out->frame_count;
}

int count_frame(READ_GOP_PARAMETER *p)
{
	int start,last;
	GOP g;

	video_stream_seek(p->p, 0, SEEK_SET);

	if(! read_gop(p, &g)){
		return -1;
	}

	start = g.start_frame;

	last_gop(p->p);

	last = read_gop(p, &g);
	last += g.start_frame;

	return last - start;
}	

GOP find_gop_with_timecode(void *p, int frame)
{
	size_t first,last, i;
	int n;

	GOP r;

	READ_GOP_PARAMETER *gp;

	gp = (READ_GOP_PARAMETER *)p;

	first = 0;
	last = gp->p->file_length;

	while(1){
		i = first + (last - first)/2;
		
		if(i == first){
			break;
		}
		
		video_stream_seek(gp->p, i, SEEK_SET);
		
		if((n =  read_gop(gp, &r)) == 0){
			last = i;
            continue;
		}
		
		if(r.start_frame > frame){
			last = i;
		}else if(r.start_frame + n > frame){
			return r;
		}else{
			first = r.offset;
		}
	}
		
	r.offset = 0;
	r.start_frame = 0;
	r.frame_count = 0;
	
	return r;		
}

