/*******************************************************************
                         GOP List Interface
 *******************************************************************/

#define GOP_LIST_C
#include "gop_list.h"

GOP_LIST *new_gop_list(VIDEO_STREAM *in, READ_PICTURE_HEADER_OPTION *opt);
void delete_gop_list(void *p);
GOP find_gop_with_gop_list(void *p, int frame);


GOP_LIST *new_gop_list(VIDEO_STREAM *in, READ_PICTURE_HEADER_OPTION *opt)
{
	int i;
	int frame;
	unsigned int code;
	unsigned int offset;
	unsigned int intra_offset;
	GOP_ENTRY *p;
	GOP_ENTRY *c;
	PICTURE_HEADER pic;

	GOP_LIST *r;

	c = NULL;
	p = NULL;
	
	frame = 0;
	intra_offset = 0;
	offset = 0;

	while(next_start_code(in)){
		code = get_bits(in, 32);
		if(code == 0x100){
			/* stream has no gop layer */
			return NULL;
		}else if(code == 0x1B8){
			offset = video_stream_tell(in) - 4;
			break;
		}
	}

	while(next_start_code(in)){
		code = get_bits(in, 32);
		if(code == 0x1B8){
			/* first gop has no intra picture */
			return NULL;
		}else if(code == 0x100){
			read_picture_header(in, &pic, opt);
			if(pic.picture_coding_type == 1){
				c = (GOP_ENTRY *)malloc(sizeof(GOP_ENTRY));
				c->prev = NULL;
				c->next = NULL;
				c->index = 0;
				c->start = frame - intra_offset + pic.temporal_reference;
				c->offset = offset;
				p = c;
				intra_offset = pic.temporal_reference;
				frame = 0;
				break;
			}
			frame += 1;
		}
	}
	
	while(next_start_code(in)){
		code = get_bits(in, 32);
		if(code == 0x100){
			read_picture_header(in, &pic, opt);
			if(pic.picture_coding_type == 1){
				c = (GOP_ENTRY *)malloc(sizeof(GOP_ENTRY));
				c->prev = p;
				c->next = NULL;
				c->index = p->index + 1;
				c->start = p->start + frame - intra_offset + pic.temporal_reference;
				p->count = c->start - p->start;
				c->offset = offset;
				p = c;
				intra_offset = pic.temporal_reference;
				frame = 0;
			}
			frame += 1;
		}else if(code == 0x1B8){
			offset = video_stream_tell(in) - 4;
		}
	}

	c->count = frame - intra_offset;

	r = (GOP_LIST *)calloc(1, sizeof(GOP_LIST));
	
	r->num_of_frame = c->start + frame;
	r->num_of_gop = c->index + 1;
	r->gops = (GOP *)malloc((r->num_of_gop)*sizeof(GOP));

	for(i=r->num_of_gop-1;i>=0;i--){
		r->gops[i].offset = c->offset;
		r->gops[i].start_frame = c->start;
		r->gops[i].frame_count = c->count;
		p = (GOP_ENTRY *)c->prev;
		free(c);
		c = p;
	}
	
	return r;
}

void delete_gop_list(void *p)
{
	GOP_LIST *w;

	w = (GOP_LIST *)p;
	
	if(w != NULL){
		if(w->gops != NULL){
			free(w->gops);
		}
		free(w);
	}
}

GOP find_gop_with_gop_list(void *p, int frame)
{
	int first, last;
	int i;

	GOP_LIST *gl;
	
	static const GOP r = {
		0, 0, 0,
	};

	gl = (GOP_LIST *)p;
	
	if(frame < gl->gops[0].start_frame || frame > gl->num_of_frame-1){
		return r;
	}

	if(gl->gops[gl->num_of_gop-1].start_frame <= frame){
		return gl->gops[gl->num_of_gop-1];
	}

	first = 0;
	last = gl->num_of_gop - 1;

	i = last/2;
	
	while(1){
		if(gl->gops[i].start_frame <= frame && gl->gops[i+1].start_frame > frame){
			return gl->gops[i];
		}else if(gl->gops[i].start_frame < frame){
			first = i;
		}else{
			last = i;
		}
		i = first + (last - first)/2;
		if(first == last){
			return r;
		}
	}
}