/*******************************************************************
                         GOP list module
 *******************************************************************/

#define GOP_LIST_C
#include "gop_list.h"

typedef struct {
    int     index;
	__int64 start;
	__int64 count;

	__int64 offset;

	void   *prev;
	void   *next;
} GOP_ENTRY;


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, __int64 frame);

static void release_gop_entries(GOP_ENTRY *last);

GOP_LIST *new_gop_list(VIDEO_STREAM *in, READ_PICTURE_HEADER_OPTION *opt)
{
	int i;
	unsigned int code;
	
	__int64 frame;
	__int64 offset;

	int gop;

	int broken_link;
	int intra_frame;

	GOP_ENTRY *p;
	GOP_ENTRY *c;
	PICTURE_HEADER pic;

	GOP_LIST *r;

	c = NULL;
	p = NULL;
	
	frame = 0;
	gop = 0;
	offset = 0;
	
	intra_frame = 0;
	broken_link = 0;

	/* 1st step, check initial I picture */
	while(next_start_code(in)){
		code = get_bits(in, 32);
		if(code == 0x100){
			
			if(gop == 0){
				offset = video_stream_tell(in) - 4;
			}
			
			read_picture_header(in, &(pic), opt);

			if(pic.has_picture_coding_extension && (pic.pc.picture_structure != 3) ){
				if(! skip_2nd_field(in, opt)){
					release_gop_entries(c);
					return NULL;
				}
			}

			if(pic.picture_coding_type == 1){
				
				c = (GOP_ENTRY *)malloc(sizeof(GOP_ENTRY));
				c->index = 0;
				c->start = 0;
				c->offset = offset;
				c->prev = NULL;

				intra_frame = 1;
				frame = 0;

				gop = 0;

				break; /* goto 2nd step */
			}
			
		}else if(code == 0x1B8){
			gop = 1;
			offset = video_stream_tell(in) - 4;
			erase_bits(in, 26);
			broken_link = get_bits(in, 1);
		}
	}

	/* 2nd step, check all rest pictures */
	while(next_start_code(in)){
		code = get_bits(in, 32);
		if(code == 0x100){
			
			if(gop == 0){
				offset = video_stream_tell(in) - 4;
			}
			
			read_picture_header(in, &(pic), opt);

			if(pic.has_picture_coding_extension && (pic.pc.picture_structure != 3) ){
				skip_2nd_field(in, opt);
			}

			if(pic.picture_coding_type == 1){
				if(intra_frame){
					c->start += frame;
					p->count = c->start - p->start;
					frame = 0;
					intra_frame = 0;
				}					
				p = c;
				c = (GOP_ENTRY *)malloc(sizeof(GOP_ENTRY));
				p->next = c;
				c->prev = p;

				c->index = p->index + 1;
				c->start = p->start + frame;
				c->offset = offset;
				p->count = frame;

				c->next = NULL;

				frame = 0;
				intra_frame = 1;
				gop = 0;
				
			}else if(pic.picture_coding_type == 2){
				if(intra_frame){
					if(broken_link){
						broken_link = 0;
						frame = 0;
					}
					c->start += frame;
					if(p){
						p->count = c->start - p->start;
					}
					frame = 2;
					intra_frame = 0;
				}else{
					frame += 1;
				}
			}else{
				frame += 1;
			}
			
		}else if(code == 0x1B8){
			gop = 1;
			offset = video_stream_tell(in) - 4;
			erase_bits(in, 26);
			broken_link = get_bits(in, 1);
		}
	}
	
	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, __int64 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;
		}
	}
}

static void release_gop_entries(GOP_ENTRY *last)
{
	GOP_ENTRY *p;
	GOP_ENTRY *c;

	c = last;

	while(c != NULL){
		p = c->prev;
		free(c);
		c = p;
	}

}
	
