#include <stdio.h>
#include <stdlib.h>

#include "config.h"
#ifdef USE_LIBMPEG2

#include "mp_msg.h"

#include "vd_internal.h"

//#undef MPEG12_POSTPROC

static vd_info_t info = 
{
	"MPEG 1/2 Video decoder libmpeg2-v0.3.1",
	"libmpeg2",
	"A'rpi & Fabian Franz",
	"Aaron & Walken",
	"native"
};

LIBVD_EXTERN(libmpeg2)

//#include "libvo/video_out.h"	// FIXME!!!

#include "libmpeg2/mpeg2.h"
#include "libmpeg2/mpeg2_internal.h"
//#include "libmpeg2/convert.h"

#include "../cpudetect.h"

// to set/get/query special features/parameters
static int control(sh_video_t *sh,int cmd,void* arg,...){
    return CONTROL_UNKNOWN;
}

// init driver
static int init(sh_video_t *sh){
    mpeg2dec_t * mpeg2dec;
    const mpeg2_info_t * info;
    int accel;

    accel = 0;
    if(gCpuCaps.hasMMX)
       accel |= MPEG2_ACCEL_X86_MMX;
    if(gCpuCaps.hasMMX2)
       accel |= MPEG2_ACCEL_X86_MMXEXT;
    if(gCpuCaps.has3DNow)
       accel |= MPEG2_ACCEL_X86_3DNOW;
    if(gCpuCaps.hasAltiVec)
       accel |= MPEG2_ACCEL_PPC_ALTIVEC;
    #ifdef HAVE_MLIB
       accel |= MPEG2_ACCEL_MLIB;
    #endif
    mpeg2_accel(accel);

    mpeg2dec = mpeg2_init ();

    if(!mpeg2dec) return 0;

    mpeg2_custom_fbuf(mpeg2dec,1); // enable DR1
    
    sh->context=mpeg2dec;

    return 1;
    //return mpcodecs_config_vo(sh,sh->disp_w,sh->disp_h,IMGFMT_YV12);
}

// uninit driver
static void uninit(sh_video_t *sh){
    mpeg2dec_t * mpeg2dec = sh->context;
    mpeg2_close (mpeg2dec);
}

static void draw_slice (void * _sh, uint8_t ** src, unsigned int y){ 
    sh_video_t* sh = (sh_video_t*) _sh;
    mpeg2dec_t* mpeg2dec = sh->context;
    const mpeg2_info_t * info = mpeg2_info (mpeg2dec);
    int stride[3];

//    printf("draw_slice() y=%d  \n",y);

    stride[0]=mpeg2dec->decoder.stride;
    stride[1]=stride[2]=mpeg2dec->decoder.uv_stride;

    mpcodecs_draw_slice(sh, (uint8_t **)src,
		stride, info->sequence->picture_width,
		(y+16<=info->sequence->picture_height) ? 16 :
		    info->sequence->picture_height-y,
		0, y);
}

// decode a frame
static mp_image_t* decode(sh_video_t *sh,void* data,int len,int flags){
    mpeg2dec_t * mpeg2dec = sh->context;
    const mpeg2_info_t * info = mpeg2_info (mpeg2dec);
    mp_image_t* mpi=NULL;
    int drop_frame, framedrop=flags&3;

    if(len<=0) return NULL; // skipped null frame
    
    // append extra 'end of frame' code:
    ((char*)data+len)[0]=0;
    ((char*)data+len)[1]=0;
    ((char*)data+len)[2]=1;
    ((char*)data+len)[3]=0xff;
    len+=4;

    mpeg2_buffer (mpeg2dec, data, data+len);
    
    while(1){
	int state=mpeg2_parse (mpeg2dec);
	switch(state){
	case -1:
	    // parsing of the passed buffer finished, return.
//	    if(!mpi) printf("\nNO PICTURE!\n");
	    return mpi;
	case STATE_SEQUENCE:
	    // video parameters inited/changed, (re)init libvo:
	    if(!mpcodecs_config_vo(sh,
		info->sequence->width,
		info->sequence->height, IMGFMT_YV12)) return 0;
	    break;
	case STATE_PICTURE: {
	    int type=info->current_picture->flags&PIC_MASK_CODING_TYPE;
	    mp_image_t* mpi;
	    
	    drop_frame = framedrop && (mpeg2dec->decoder.coding_type == B_TYPE);
            drop_frame |= framedrop>=2; // hard drop
            if (drop_frame) {
               mpeg2_skip(mpeg2dec, 1);
	       //printf("Dropping Frame ...\n");
	       break;
	    }
            mpeg2_skip(mpeg2dec, 0); //mpeg2skip skips frames until set again to 0

	    // get_buffer "callback":
	     mpi=mpcodecs_get_image(sh,MP_IMGTYPE_IPB,
		(type==PIC_FLAG_CODING_TYPE_B)
		? ((!framedrop && vd_use_slices &&
		    (info->current_picture->flags&PIC_FLAG_PROGRESSIVE_FRAME)) ?
			    MP_IMGFLAG_DRAW_CALLBACK:0)
		: (MP_IMGFLAG_PRESERVE|MP_IMGFLAG_READABLE),
		(info->sequence->picture_width+15)&(~15),
		(info->sequence->picture_height+15)&(~15) );
	    if(!mpi) return 0; // VO ERROR!!!!!!!!
	    mpeg2_set_buf(mpeg2dec, mpi->planes, mpi);
	    if (info->current_picture->flags&PIC_FLAG_TOP_FIELD_FIRST)
		mpi->fields |= MP_IMGFIELD_TOP_FIRST;
	    else mpi->fields &= ~MP_IMGFIELD_TOP_FIRST;
	    if (info->current_picture->flags&PIC_FLAG_REPEAT_FIRST_FIELD)
		mpi->fields |= MP_IMGFIELD_REPEAT_FIRST;
	    else mpi->fields &= ~MP_IMGFIELD_REPEAT_FIRST;
	    mpi->fields |= MP_IMGFIELD_ORDERED;

#ifdef MPEG12_POSTPROC
	    if(!mpi->qscale){
		mpi->qstride=(info->sequence->picture_width+15)>>4;
		mpi->qscale=malloc(mpi->qstride*((info->sequence->picture_height+15)>>4));
	    }
	    mpeg2dec->decoder.quant_store=mpi->qscale;
	    mpeg2dec->decoder.quant_stride=mpi->qstride;
	    mpi->pict_type=type; // 1->I, 2->P, 3->B
	    mpi->qscale_type= 1;
#endif

	    if(mpi->flags&MP_IMGFLAG_DRAW_CALLBACK &&
		!(mpi->flags&MP_IMGFLAG_DIRECT)){
		   // nice, filter/vo likes draw_callback :)
		    mpeg2dec->decoder.convert=draw_slice;
		    mpeg2dec->decoder.fbuf_id=sh;
		} else
		    mpeg2dec->decoder.convert=NULL;
	    break;
	}
	case STATE_SLICE:
	case STATE_END:
	    // decoding done:
	    if(mpi) printf("AJAJJJJJJJJ2!\n");
	    if(info->display_fbuf) mpi=info->display_fbuf->id;
//	    return mpi;
	}
    }
}
#endif
