/* 
 *	Copyright (C) Chia-chen Kuo - April 2001
 *
 *  This file is part of DVD2AVI, a free MPEG-2 decoder
 *	
 *  DVD2AVI 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.
 *   
 *  DVD2AVI 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 <exception>
#include <math.h>

#include "dvdfilters.h"

#include "conf.h"
#include "store.h"
#include "memcopy.h"
#include "../Error.h"


static char l_szBuffer[_MAX_PATH];


#define MAX_AVI_SIZE	2073600000

static void Store_RGB24(unsigned char *src[]);
static void Store_YUY2(unsigned char *src[]);
static void Store_YUV(unsigned char *src[]);
static void Flush_RGB24(void);
static void Flush_YUY2(void);
static void Flush_YUV(unsigned char* src[]);
static void AVIKill(void);

PAVIFILE pfile;
PAVISTREAM ps, psCompressed;
AVICOMPRESSOPTIONS opts;
AVISTREAMINFO strinfo;
ICCOMPRESSFRAMES iccf;
COMPVARS compvars;
BITMAPINFOHEADER birgb;
BITMAPINFOHEADER biyuv;

static char VideoOut[_MAX_PATH];
static int AVI_Init, avi_count;
static int TFF, RFF, TFB, BFB, playback, old_playback, frame_type;

static char *FrameType[3] = 
{
	"", "Interlaced", "Progressive"
};

void Write_Frame_Init()
{
	int i;

	AVI_Init = 1; TFB = BFB = 0; old_playback = 0;
	g_PicInfo.avi_size = 0; avi_count = 1;
	g_PicInfo.frame_count = 0;
	g_PicInfo.kframe_count = 0;
	playback = 0;

	g_PicInfo.Clip_Width = g_PicInfo.Resize_Width = g_PicInfo.Coded_Picture_Width;
	g_PicInfo.Clip_Height = g_PicInfo.Resize_Height = g_PicInfo.Coded_Picture_Height;
	
	if (g_PicInfo.Aspect_Change == ASPECT_UNKNOWN)
	{
		g_PicInfo.Aspect_Change = ASPECT_43;
		g_OutFunctions.AutoSize();
	}

	if (g_PicInfo.Clip_Top || g_PicInfo.Clip_Bottom || g_PicInfo.Clip_Left || g_PicInfo.Clip_Right)
	{
		g_PicInfo.Clip_Width -= g_PicInfo.Clip_Left+g_PicInfo.Clip_Right;
		g_PicInfo.Clip_Height -= g_PicInfo.Clip_Top+g_PicInfo.Clip_Bottom;
		g_PicInfo.Resize_Width = g_PicInfo.Clip_Width;
		g_PicInfo.Resize_Height = g_PicInfo.Clip_Height;
	}
	
	if (g_PicInfo.Squeeze_Width || g_PicInfo.Squeeze_Height)
	{
		g_PicInfo.Resize_Width -= g_PicInfo.Squeeze_Width;
		g_PicInfo.Resize_Height -= g_PicInfo.Squeeze_Height;
	}

	InitFilters();

	g_OutFunctions.ResizeWindow(g_PicInfo.Resize_Width, g_PicInfo.Resize_Height);
	
	ZeroMemory(&birgb, sizeof(BITMAPINFOHEADER));
	birgb.biSize = sizeof(BITMAPINFOHEADER);
	birgb.biWidth = g_PicInfo.Resize_Width;
	birgb.biHeight = g_PicInfo.Resize_Height;
	birgb.biPlanes = 1;
	birgb.biBitCount = 24;
	birgb.biCompression = BI_RGB;
	birgb.biSizeImage = g_PicInfo.Resize_Width * g_PicInfo.Resize_Height * 3;

	ZeroMemory(&biyuv, sizeof(BITMAPINFOHEADER));
	biyuv = birgb;
	biyuv.biBitCount = 16;
	biyuv.biCompression = mmioFOURCC('Y','U','Y','2');
	biyuv.biSizeImage = g_PicInfo.Resize_Width * g_PicInfo.Resize_Height * 2;

	if (process.locate==LOCATE_RIP && g_Flags.FO_Flag==FO_SWAP)
	{
		if (TFF)
			BFB = 1;
		else
			TFB = 1;

		// mask
		ZeroMemory(g_PicInfo.rgb24, g_PicInfo.Resize_Width * g_PicInfo.Resize_Height * 3);
		for (i=0; i<g_PicInfo.Resize_Width * g_PicInfo.Resize_Height * 2; i+=2)
		{
			g_PicInfo.yuy2[i] = 0;
			g_PicInfo.yuy2[i+1] = 128;
		}
	}
}

void Write_Frame(unsigned char *src[], D2VData d2v, DWORD frame)
{
//	if (src)
	{
		int repeat;
		if (g_Flags.Fault_Flag)
		{
			if (g_Flags.Fault_Flag < CRITICAL_ERROR_LEVEL)
			{
				//g_InfoFunctions.Info("V.E.!");
				g_Flags.Fault_Flag = 0;		// fault tolerance
			}
			else
			{
				if (g_Flags.AVI_Flag)
					AVIKill();
				g_OutFunctions.ThreadKill();
				throw DVD_EOF();
			}
		}

		frame_type = d2v.pf;
		TFF = d2v.trf>>1;
		RFF = d2v.trf & 0x01;

		if (!frame)
		{
			Write_Frame_Init();
		}

		repeat = DetectVideoType(frame, d2v.trf);

		if (g_Flags.FO_Flag!=FO_FILM || repeat)
		{
			switch(g_Flags.Store_Flag)
			{
			case STORE_YUY2:
				Store_YUY2(src);
				break;
			case STORE_RGB24:
				Store_RGB24(src);
				break;
			case STORE_YUV:
				Store_YUV(src);
				break;
			}
		}

		if (g_Flags.FO_Flag==FO_FILM && repeat==2)
		{
			switch(g_Flags.Store_Flag)
			{
			case STORE_YUY2:
				Store_YUY2(src);
				break;
			case STORE_RGB24:
				Store_RGB24(src);
				break;
			case STORE_YUV:
				Store_YUV(src);
				break;
			}
		}
	}
}

static void Store_RGB24(unsigned char *src[])
{
	if (g_Flags.AVI_Flag && AVI_Init)
	{
		AVI_Init = 0;
	}

	if (g_DVDGlobals.chroma_format==CHROMA420)
	{
		conv420to422(frame_type, src[1], g_PicInfo.u422);
		conv420to422(frame_type, src[2], g_PicInfo.v422);

		conv422to444(g_PicInfo.u422, g_PicInfo.u444 );
		conv422to444(g_PicInfo.v422, g_PicInfo.v444 );
	}
	else
	{
		conv422to444(src[1], g_PicInfo.u444 );
		conv422to444(src[2], g_PicInfo.v444 );
	}

	unsigned char *y444;

	/*if (g_PicInfo.LumGain!=128 || g_PicInfo.LumOffset!=0)
	{
		Luminance_Filter(src[0], g_PicInfo.lum);
		y444 = g_PicInfo.lum;
	}
	else*/
		y444 = src[0];

	if (BFB)
	{
		conv444toRGB24odd(y444, g_PicInfo.u444, g_PicInfo.v444, g_PicInfo.rgb24 );
		TFB = 1;
		Flush_RGB24();
		conv444toRGB24even(y444, g_PicInfo.u444, g_PicInfo.v444, g_PicInfo.rgb24 );
		BFB = 1;
		Flush_RGB24();
	}
	else
	{
		conv444toRGB24even(y444, g_PicInfo.u444, g_PicInfo.v444, g_PicInfo.rgb24 );
		BFB = 1;
		Flush_RGB24();
		conv444toRGB24odd(y444, g_PicInfo.u444, g_PicInfo.v444, g_PicInfo.rgb24 );
		TFB = 1;
		Flush_RGB24();
	}

	if (g_Flags.FO_Flag!=FO_FILM && RFF)
	{
		if (TFF)
		{
			TFB = 1;
			Flush_RGB24();
		}
		else
		{
			BFB = 1;
			Flush_RGB24();
		}
	}
}

static void Flush_RGB24(void)
{
	
	DVD_T("Flush_RGB24 TFB = " << TFB << " BFB = " << BFB)
	if (TFB & BFB)
	{
		ApplyFilters();
		if (g_Flags.Display_Flag)
			g_OutFunctions.RenderRGB24(GetFilteredOutput());
		playback++;
		TFB = BFB = 0;
	}
}

static void Store_YUY2(unsigned char *src[])
{
	if (g_Flags.AVI_Flag && AVI_Init)
	{
		AVI_Init = 0; 
	}

	if (g_DVDGlobals.chroma_format==CHROMA420)
	{
		conv420to422(frame_type, src[1], g_PicInfo.u422);
		conv420to422(frame_type, src[2], g_PicInfo.v422);
	}
	else
	{
		g_PicInfo.u422 = src[1];
		g_PicInfo.v422 = src[2];
	}

	unsigned char *y444;
	
	/*if (g_PicInfo.LumGain!=128 || g_PicInfo.LumOffset!=0)
	{
		Luminance_Filter(src[0], g_PicInfo.lum);
		y444 = g_PicInfo.lum;
	}
	else*/
		y444 = src[0];

	if (BFB)
	{
		conv422toyuy2odd(y444, g_PicInfo.u422, g_PicInfo.v422, g_PicInfo.yuy2 );
		TFB = 1;
		Flush_YUY2();

		conv422toyuy2even(y444, g_PicInfo.u422, g_PicInfo.v422, g_PicInfo.yuy2 );
		BFB = 1;
		Flush_YUY2();
	}
	else
	{
		conv422toyuy2even(y444, g_PicInfo.u422, g_PicInfo.v422, g_PicInfo.yuy2 );
		BFB = 1;
		Flush_YUY2();

		conv422toyuy2odd(y444, g_PicInfo.u422, g_PicInfo.v422, g_PicInfo.yuy2 );
		TFB = 1;
		Flush_YUY2();
	}

	if (g_Flags.FO_Flag!=FO_FILM && RFF)
	{
		if (TFF)
		{
			TFB = 1;
			Flush_YUY2();
		}
		else
		{
			BFB = 1;
			Flush_YUY2();
		}
	}
}


static void Flush_YUY2(void)
{
	if (TFB & BFB)
	{
		ApplyFilters();
		playback++;
		TFB = BFB = 0;
	}
}
static void Store_YUV(unsigned char *src[])
{
	if (g_Flags.AVI_Flag && AVI_Init)
	{
		AVI_Init = 0; 
	}

	if (BFB)
	{
		TFB = 1;
		Flush_YUV(src);

		BFB = 1;
		Flush_YUV(src);
	}
	else
	{
		BFB = 1;
		Flush_YUV(src);

		TFB = 1;
		Flush_YUV(src);
	}

	if (g_Flags.FO_Flag!=FO_FILM && RFF)
	{
		if (TFF)
		{
			TFB = 1;
			Flush_YUV(src);
		}
		else
		{
			BFB = 1;
			Flush_YUV(src);
		}
	}
}


static void Flush_YUV(unsigned char* src[])
{
	if (TFB & BFB)
	{
		ApplyFilters(src);
		playback++;
		TFB = BFB = 0;
	}
}

int DetectVideoType(int frame, int trf)
{
	static int Old_TRF, Repeat_On, Repeat_Off, Repeat_Init;

	if (frame)
	{
		if ((trf & 3) == ((Old_TRF+1) & 3))
			g_DVDGlobals.FILM_Purity++;
		else
			g_DVDGlobals.NTSC_Purity++;
	}
	else
		g_DVDGlobals.Video_Type = g_DVDGlobals.FILM_Purity = g_DVDGlobals.NTSC_Purity = Repeat_On = Repeat_Off = Repeat_Init = 0; 

	Old_TRF = trf;

	if (trf & 1)
		Repeat_On ++;
	else
		Repeat_Off ++;

	if (Repeat_Init)
	{
		if (Repeat_Off-Repeat_On == 5)
		{
			Repeat_Off = Repeat_On = 0;
			return 0;
		}
		else if (Repeat_On-Repeat_Off == 5)
		{
			Repeat_Off = Repeat_On = 0;
			return 2;
		}
	}
	else
	{
		if (Repeat_Off-Repeat_On == 3)
		{
			Repeat_Off = Repeat_On = 0;
			Repeat_Init = 1;
			return 0;
		}
		else if (Repeat_On-Repeat_Off == 3)
		{
			Repeat_Off = Repeat_On = 0;
			Repeat_Init = 1;
			return 2;
		}
	}

	return 1;
}

static void AVIKill(void)
{
	AVI_Init = 1;
	//g_PicInfo.frame_count = 0;
}
