/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Jean Le Feuvre 2000-2003 
 *					All rights reserved
 *
 *  This file is part of GPAC / codec pack plugin
 *
 *  GPAC 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, or (at your option)
 *  any later version.
 *   
 *  GPAC 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 GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */


#include <gpac/m4_decoder.h>

#include <gpac/m4_author.h>

#include "xvid_wce/xvid.h"


/*beerk*/
static Bool xvid_is_init = 0;

typedef struct
{
	void *codec;
	/*no support for scalability in XVID yet*/
	u16 ES_ID;
	u32 width, height, out_size;
	u32 cb_size, cb_trig;
	Bool first_frame;
	s32 base_filters;
	Float FPS;
} XVIDDec;

#define XVIDCTX()	XVIDDec *ctx = (XVIDDec *) (ifcg->privateStack)


static M4Err XVID_AttachStream(BaseDecoder *ifcg, u16 ES_ID, unsigned char *decSpecInfo, u32 decSpecInfoSize, u16 DependsOnES_ID, u32 objectTypeIndication, Bool UpStream)
{
	M4VDecoderSpecificInfo dsi;
	M4Err e;
	unsigned char *ptr;
	unsigned long pitch;

	XVIDCTX();

	if (ctx->ES_ID && ctx->ES_ID!=ES_ID) return M4NotSupported;
	if (!decSpecInfoSize || !decSpecInfo) return M4NonCompliantBitStream;

	/*decode DSI*/
	e = M4V_GetConfig((char *) decSpecInfo, decSpecInfoSize, &dsi);
	if (e) return e;
	if (!dsi.width || !dsi.height) return M4NonCompliantBitStream;

	ctx->codec =  InitCodec(dsi.width, dsi.height, FOUR_CHAR_INT('x', 'v', 'i', 'd'));

	DecodeFrame(ctx->codec, decSpecInfo, decSpecInfoSize, ptr, ptr, ptr, pitch);

	/*note that this may be irrelevant when used through systems (FPS is driven by systems CTS)*/
	ctx->FPS = dsi.clock_rate;
	ctx->FPS /= 1000;
	if (!ctx->FPS) ctx->FPS = 30.0f;
	ctx->width = dsi.width;
	ctx->height = dsi.height;
	ctx->ES_ID = ES_ID;
	ctx->first_frame = 1;
	/*output in YV12 only - let the player handle conversion*/
	ctx->out_size = 3 * ctx->width * ctx->height / 2;
	return M4OK;
}
static M4Err XVID_DetachStream(BaseDecoder *ifcg, u16 ES_ID)
{
	XVIDCTX();
	if (ctx->ES_ID != ES_ID) return M4BadParam;
	if (ctx->codec) CloseCodec(ctx->codec);
	ctx->codec = NULL;
	ctx->ES_ID = 0;
	ctx->width = ctx->height = ctx->out_size = 0;
	return M4OK;
}
static M4Err XVID_GetCapabilities(BaseDecoder *ifcg, CapObject *capability)
{
	XVIDCTX();

	switch (capability->CapCode) {
	case CAP_WIDTH:
		capability->cap.valueInt = ctx->width;
		break;
	case CAP_HEIGHT:
		capability->cap.valueInt = ctx->height;
		break;
	case CAP_STRIDE:
		capability->cap.valueInt = ctx->width;
		break;
	case CAP_FPS:
		capability->cap.valueFloat = ctx->FPS;
		break;
	case CAP_OUTPUTSIZE:
		capability->cap.valueInt = ctx->out_size;
		break;
	case CAP_COLORMODE:
		capability->cap.valueInt = M4PF_YV12;
		break;
	/*no postprocessing API for xvid*/
	case CAP_VID_POSTPROC:
		capability->cap.valueInt = 0;
		break;
	case CAP_BUFFER_MIN:
		capability->cap.valueInt = ctx->cb_trig;
		break;
	case CAP_BUFFER_MAX:
		capability->cap.valueInt = ctx->cb_size;
		break;
	/*no support for shape coding*/
	case CAP_VID_SHAPE:
		capability->cap.valueInt = 0;
		break;
	/*by default we use 4 bytes padding (otherwise it happens that XviD crashes on some videos...)*/
	case CAP_PADDING_BYTES:
		capability->cap.valueInt = 32;
		break;
	/*XviD performs frame reordering internally*/
	case CAP_VID_REORDER:
		capability->cap.valueInt = 1;
		break;
	case CAP_CODEC_WANTSTHREAD:
	{
		const char *sOpt = PMI_GetOpt(ifcg, "XviD", "Threaded");
		capability->cap.valueInt = (sOpt && stricmp(sOpt, "yes")) ? 1 : 0;
	}
		break;
	/*not known at our level...*/
	case CAP_CU_DURATION:
	default:
		capability->cap.valueInt = 0;
		break;
	}
	return M4OK;
}
static M4Err XVID_SetCapabilities(BaseDecoder *ifcg, CapObject capability)
{
	/*return unsupported to avoid confusion by the player (like color space changing ...) */
	return M4NotSupported;
}
static M4Err XVID_ProcessData(MediaDecoder *ifcg, 
		unsigned char *inBuffer, u32 inBufferLength,
		u16 ES_ID,
		unsigned char *outBuffer, u32 *outBufferLength,
		u8 PaddingBits, u32 mmlevel)
{
	unsigned char *pY, *pU, *pV;
	u32 i, uv_w, half_h;
	unsigned long pitch;
	XVIDCTX();

	/*check not using scalabilty*/
	if (ES_ID != ctx->ES_ID) return M4BadParam;

	if (*outBufferLength < ctx->out_size) {
		*outBufferLength = ctx->out_size;
		return M4BufferTooSmall;
	}

	if (!DecodeFrame(ctx->codec, inBuffer, inBufferLength, pY, pU, pV, pitch)) {
		*outBufferLength = 0;
		return M4NonCompliantBitStream;
	}

	/*dispatch nothing if seeking or droping*/
	switch (mmlevel) {
	case MM_LEVEL_SEEK:
	case MM_LEVEL_DROP:
		*outBufferLength = 0;
		return M4OK;
	default:
		break;
	}
	*outBufferLength = ctx->out_size;
	for (i=0; i<ctx->height; i++) {
		unsigned char *src = pY + pitch*i;
		unsigned char *dst = outBuffer + ctx->width*i;
		memcpy(dst, src, sizeof(char) * ctx->width);
	}
	outBuffer += ctx->width * ctx->height;
	half_h = ctx->height/2;
	uv_w = ctx->width/2;
	for (i=0; i<half_h; i++) {
		unsigned char *src = pU + pitch/2*i;
		unsigned char *dst = outBuffer + i*uv_w;
		memcpy(dst, src, sizeof(char) * uv_w);
	}
	outBuffer += ctx->width * ctx->height / 4;
	for (i=0; i<half_h; i++) {
		unsigned char *src = pV + pitch/2*i;
		unsigned char *dst = outBuffer + i*uv_w;
		memcpy(dst, src, sizeof(char) * uv_w);
	}

	return M4OK;
}

static const char *XVID_GetCodecName(BaseDecoder *dec)
{
	return "XviD 1.0 for WinCE";
}

static Bool XVID_CanHandleStream(BaseDecoder *dec, u32 StreamType, u32 ObjectType, unsigned char *decSpecInfo, u32 decSpecInfoSize, u32 PL)
{
	if (StreamType != M4ST_VISUAL) return 0;
	switch (ObjectType) {
	case 0x20:
		return 1;
	/*cap query*/
	case 0:
		return 1;
	}
	return 0;
}



BaseDecoder *NewXVIDDec()
{
	MediaDecoder *ifcd;
	XVIDDec *dec;

	ifcd = (MediaDecoder*) malloc(sizeof(MediaDecoder));
	memset(ifcd, 0, sizeof(MediaDecoder));
	
	dec = (XVIDDec*) malloc(sizeof(XVIDDec));
	memset(dec, 0, sizeof(XVIDDec));

	dec->cb_size = 4;
	dec->cb_trig = 1;
	/*setup our own interface*/	
	ifcd->AttachStream = XVID_AttachStream;
	ifcd->DetachStream = XVID_DetachStream;
	ifcd->GetCapabilities = XVID_GetCapabilities;
	ifcd->SetCapabilities = XVID_SetCapabilities;
	ifcd->GetName = XVID_GetCodecName;
	ifcd->ProcessData = XVID_ProcessData;
	ifcd->CanHandleStream = XVID_CanHandleStream;
	ifcd->privateStack = dec;
	M4_REG_PLUG(ifcd, M4MEDIADECODERINTERFACE, "XviD for CE Decoder", "gpac distribution", 0)
	return (BaseDecoder *) ifcd;
}

void DeleteXVIDDec(BaseDecoder *ifcg)
{
	XVIDCTX();
	if (ctx->codec) CloseCodec(ctx->codec);
	free(ctx);
	free(ifcg);
}


Bool QueryInterface(u32 InterfaceType) 
{
	if (InterfaceType == M4MEDIADECODERINTERFACE) return 1;
	return 0;
}

void *LoadInterface(u32 InterfaceType) 
{
	if (InterfaceType == M4MEDIADECODERINTERFACE) return NewXVIDDec();
	return NULL;
}

void ShutdownInterface(void *ifce)
{
	BaseDecoder *ptr = (BaseDecoder*)ifce;
	switch (ptr->InterfaceType) {
	case M4MEDIADECODERINTERFACE: 
		DeleteXVIDDec(ptr);
		break;
	}
}
