/*
 * decode.c
 * Copyright (C) 1999-2001 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
 *
 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
 *
 * mpeg2dec is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * mpeg2dec is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"

#include <stdio.h>
#include <string.h>	/* memcpy/memset, try to remove */
#include <stdlib.h>

#include "inttypes.h"

#include "video_out.h"
#include "mpeg2.h"
#include "mpeg2_internal.h"
#include "mm_accel.h"
#include "attributes.h"

#ifdef HAVE_MEMALIGN
/* some systems have memalign() but no declaration for it */
void * memalign (size_t align, size_t size);
#else
/* assume malloc alignment is sufficient */
#define memalign(align,size) malloc (size)
#endif

mpeg2_config_t config;

#ifdef REALTIME
#include "timer1.h"
extern int video_step;
int old_time;
int video_step;
//os_t old_time;
int waitIframe = 0;
#endif

void mpeg2_init (mpeg2dec_t * mpeg2dec, uint32_t mm_accel,
		 vo_instance_t * output)
{
    static int do_init = 1;

    if (do_init) {
	do_init = 0;
	config.flags = mm_accel;
	idct_init ();
	motion_comp_init ();
    }

    mpeg2dec->chunk_buffer = memalign (16, 224 * 1024 + 4);
    mpeg2dec->picture = memalign (16, sizeof (picture_t));

    mpeg2dec->shift = 0;
    mpeg2dec->is_sequence_needed = 1;
    mpeg2dec->drop_flag = 0;
    mpeg2dec->drop_frame = 0;
    mpeg2dec->in_slice = 0;
    mpeg2dec->output = output;
    mpeg2dec->chunk_ptr = mpeg2dec->chunk_buffer;
    mpeg2dec->code = 0xff;

    memset (mpeg2dec->picture, 0, sizeof (picture_t));

#ifdef REALTIME
    old_time=read_timer1();
#endif

    /* initialize supstructures */
    header_state_init (mpeg2dec->picture);
}

static int parse_chunk (mpeg2dec_t * mpeg2dec, int code, uint8_t * buffer)
{
    picture_t * picture;
    int is_frame_done;
    int temp1;

    /* wait for sequence_header_code */
    if (mpeg2dec->is_sequence_needed && (code != 0xb3))
	return 0;

    stats_header (code, buffer);

    picture = mpeg2dec->picture;
    is_frame_done = mpeg2dec->in_slice && ((!code) || (code >= 0xb0));

    if (is_frame_done) {
	mpeg2dec->in_slice = 0;
	if (((picture->picture_structure == FRAME_PICTURE) ||
	     (picture->second_field)) &&
	    (!(mpeg2dec->drop_frame))) {
#ifdef REALTIME
        while (read_timer1()-old_time<video_step) { }
//        old_time=os_read_monotonic_time();
#endif
	    vo_draw ((picture->picture_coding_type == B_TYPE) ?
		     picture->current_frame :
		     picture->forward_reference_frame);
#ifdef REALTIME
        mpeg2dec->drop_flag+=(read_timer1()-old_time)/video_step;
//        mpeg2dec->drop_flag+=2;
#endif
	}
#ifdef REALTIME
        old_time=read_timer1();
        if (mpeg2dec->drop_flag>0) mpeg2dec->drop_flag--;
//        else mpeg2dec->drop_flag=1;
#endif
    }

    switch (code) {
    case 0x00:	/* picture_start_code */
	if (header_process_picture_header (picture, buffer)) {
	    fprintf (stderr, "bad picture header\n");
	    exit (1);
	}

/*        if (mpeg2dec->drop_flag && (picture->picture_coding_type == P_TYPE)){
          waitIframe = 1;
	    picture->forward_reference_frame =
		vo_get_frame (mpeg2dec->output,
			      VO_PREDICTION_FLAG | VO_BOTH_FIELDS);
	    picture->backward_reference_frame =
		vo_get_frame (mpeg2dec->output,
			      VO_PREDICTION_FLAG | VO_BOTH_FIELDS);

        }
        if ((picture->picture_coding_type == I_TYPE) || (picture->picture_coding_type == P_TYPE)){
          waitIframe = 0;
        }
*/

	mpeg2dec->drop_frame =
//	    mpeg2dec->drop_flag && (picture->picture_coding_type == B_TYPE ||
//	    picture->picture_coding_type == P_TYPE) || waitIframe;
	    mpeg2dec->drop_flag && (picture->picture_coding_type == B_TYPE);
	break;

    case 0xb3:	/* sequence_header_code */
	if (header_process_sequence_header (picture, buffer)) {
	    fprintf (stderr, "bad sequence header\n");
	    exit (1);
	}
	if (mpeg2dec->is_sequence_needed) {
	    mpeg2dec->is_sequence_needed = 0;
	    if (vo_setup (mpeg2dec->output, picture->coded_picture_width,
			  picture->coded_picture_height)) {
		fprintf (stderr, "display setup failed\n");
		exit (1);
	    }
	    picture->forward_reference_frame =
		vo_get_frame (mpeg2dec->output,
			      VO_PREDICTION_FLAG | VO_BOTH_FIELDS);
	    picture->backward_reference_frame =
		vo_get_frame (mpeg2dec->output,
			      VO_PREDICTION_FLAG | VO_BOTH_FIELDS);
	}
	mpeg2dec->frame_rate_code = picture->frame_rate_code;	/* FIXME */
	switch (picture->frame_rate_code) {
	case 1: /* 23.976 fps */
	  video_step = 41; //39130;
	  break;
	case 2: /* 24 fps */
	  video_step = 41; //37500;
	  break;
	case 3: /* 25 fps */
	  video_step = 40; //36000;
	  break;
	case 4: /* 29.97 fps */
	  video_step = 33; //30030;
	  break;
	case 5: /* 30 fps */
	  video_step = 33; //30000;
	  break;
	case 6: /* 50 fps */
	  video_step = 20; //18000;
	  break;
	case 7: /* 59.94 fps */
	  video_step = 16; //15250;
	  break;
	case 8: /* 60 fps */
	  video_step = 16; //15090;
	  break;
	default:
	  fprintf (stderr, "invalid/unknown frame rate code : %d \n",
		   picture->frame_rate_code);
	  return 0;
	 /*  exit (1); CHECKME */
	}
//	video_step=40;
	break;

    case 0xb5:	/* extension_start_code */
	if (header_process_extension (picture, buffer)) {
	    fprintf (stderr, "bad extension\n");
	    exit (1);
	}
	break;

    default:
	if (code >= 0xb9)
	    fprintf (stderr, "stream not demultiplexed ?\n");

	if (code >= 0xb0)
	    break;

	if (!(mpeg2dec->in_slice)) {
	    mpeg2dec->in_slice = 1;

	    if (picture->second_field)
		vo_field (picture->current_frame, picture->picture_structure);
	    else {
		if (picture->picture_coding_type == B_TYPE)
		    picture->current_frame =
			vo_get_frame (mpeg2dec->output,
				      picture->picture_structure);
		else {
		    picture->current_frame =
			vo_get_frame (mpeg2dec->output,
				      (VO_PREDICTION_FLAG |
				       picture->picture_structure));
		    picture->forward_reference_frame =
			picture->backward_reference_frame;
		    picture->backward_reference_frame = picture->current_frame;
		}
	    }
	}

	if (!(mpeg2dec->drop_frame)) {
	    slice_process (picture, code, buffer);

#ifdef ARCH_X86
	    if (config.flags & MM_ACCEL_X86_MMX)
		emms ();
#endif
	}
    }

    return is_frame_done;
}

int mpeg2_decode_data (mpeg2dec_t * mpeg2dec, uint8_t * current, uint8_t * end)
{
    uint32_t shift;
    uint8_t * chunk_ptr;
    uint8_t byte;
    int ret = 0;

    shift = mpeg2dec->shift;
    chunk_ptr = mpeg2dec->chunk_ptr;

    while (current != end) {
	while (1) {
	    byte = *current++;
	    if (shift != 0x00000100) {
		*chunk_ptr++ = byte;
		shift = (shift | byte) << 8;
		if (current != end)
		    continue;
		mpeg2dec->chunk_ptr = chunk_ptr;
		mpeg2dec->shift = shift;
		return ret;
	    }
	    break;
	}

	/* found start_code following chunk */

	ret += parse_chunk (mpeg2dec, mpeg2dec->code, mpeg2dec->chunk_buffer);

	/* done with header or slice, prepare for next one */

	mpeg2dec->code = byte;
	chunk_ptr = mpeg2dec->chunk_buffer;
	shift = 0xffffff00;
    }
    mpeg2dec->chunk_ptr = chunk_ptr;
    mpeg2dec->shift = shift;
    return ret;
}

void mpeg2_close (mpeg2dec_t * mpeg2dec)
{
    static uint8_t finalizer[] = {0,0,1,0};

    mpeg2_decode_data (mpeg2dec, finalizer, finalizer+4);

    if (! (mpeg2dec->is_sequence_needed))
	vo_draw (mpeg2dec->picture->backward_reference_frame);

    if (mpeg2dec->chunk_buffer) free (mpeg2dec->chunk_buffer);
    if (mpeg2dec->picture) free (mpeg2dec->picture);
}

void mpeg2_drop (mpeg2dec_t * mpeg2dec, int flag)
{
    mpeg2dec->drop_flag = flag;
}
