/*******************************************************************
           Killer Tomate - MPEG-2 VIDEO VFAPI Plug-In
 *******************************************************************/
#include <windows.h>
#include <string.h>
#include <stdio.h>

#include "vfapi.h"
#include "mpeg2video.h"

HRESULT _stdcall vfGetPluginInfo(LPVF_PluginInfo info);
HRESULT _stdcall vfGetPluginFunc(LPVF_PluginFunc func);

HRESULT _stdcall open_file(char *path, LPVF_FileHandle out);
HRESULT _stdcall close_file(VF_FileHandle p);
HRESULT _stdcall get_file_info(VF_FileHandle in, LPVF_FileInfo out);
HRESULT _stdcall get_stream_info(VF_FileHandle in, DWORD s, void *out);
HRESULT _stdcall read_data(VF_FileHandle in, DWORD s, void *out);

HRESULT _stdcall vfGetPluginInfo(LPVF_PluginInfo info)
{
	if(info == NULL){
		return VF_ERROR;
	}

	if(info->dwSize != sizeof(VF_PluginInfo)){
		return VF_ERROR;
	}

	info->dwAPIVersion = 1;
	info->dwVersion = 1;
	info->dwSupportStreamType = VF_STREAM_VIDEO;

	strcpy(info->cPluginInfo, "MPEG-2 VIDEO Plug-In");
	strcpy(info->cFileType, "MPEG-2 Stream (*.m2p;*.mpg;*.m2v;*.vob)|*.m2p;*.mpg;*.m2v;*.vob");

	return VF_OK;
}

HRESULT _stdcall vfGetPluginFunc(LPVF_PluginFunc func)
{
	if(func == NULL){
		return VF_ERROR;
	}

	if(func->dwSize != sizeof(VF_PluginFunc)){
		return VF_ERROR;
	}

	func->OpenFile = open_file;
	func->CloseFile = close_file;
	func->GetFileInfo = get_file_info;
	func->GetStreamInfo = get_stream_info;
	func->ReadData = read_data;

	return VF_OK;
}

HRESULT _stdcall open_file(char *path, LPVF_FileHandle out)
{
	MPEG2VIDEO **w;

	w = (MPEG2VIDEO **)out;
	
	*w = (MPEG2VIDEO *)malloc(sizeof(MPEG2VIDEO));
	if(*w == NULL){
		return VF_ERROR;
	}

	if(! open_mpeg2video(path, *w)){
		free(*w);
		*w = NULL;
		return VF_ERROR;
	}

	return VF_OK;
}

HRESULT _stdcall close_file(VF_FileHandle p)
{
	MPEG2VIDEO *w;

	w = (MPEG2VIDEO *)p;

	close_mpeg2video(w);

	free(w);
	
	return VF_OK;
}

HRESULT _stdcall get_file_info(VF_FileHandle in, LPVF_FileInfo out)
{
	UNREFERENCED_PARAMETER(in);
	
	if(out == NULL){
		return VF_ERROR;
	}

	if(out->dwSize != sizeof(VF_FileInfo)){
		return VF_ERROR;
	}

	out->dwHasStreams = VF_STREAM_VIDEO;

	return VF_OK;
}

HRESULT _stdcall get_stream_info(VF_FileHandle in, DWORD s, void *out)
{
	MPEG2VIDEO *w;
	LPVF_StreamInfo_Video r;
	
	if(s != VF_STREAM_VIDEO){
		return VF_ERROR;
	}

	w = (MPEG2VIDEO *)in;
	r = (LPVF_StreamInfo_Video)out;

	if(r == NULL){
		return VF_ERROR;
	}
	
	if(r->dwSize != sizeof(VF_StreamInfo_Video)){
		return VF_ERROR;
	}

	r->dwLength = w->total;
	r->dwRate = w->rate;
	r->dwScale = w->scale;
	r->dwWidth = w->bgr_prm.width;
	r->dwHeight = w->bgr_prm.height;
	r->dwBitCount = 24;

	return VF_OK;
}
	
HRESULT _stdcall read_data(VF_FileHandle in, DWORD s, void *out)
{
	
	MPEG2VIDEO *w;

	LPVF_ReadData_Video r;

	OUT_BUFFER_ELEMENT *top;
	OUT_BUFFER_ELEMENT *bottom;
	
	unsigned char *dst;
	
	if(s != VF_STREAM_VIDEO){
		return VF_ERROR;
	}

	w = (MPEG2VIDEO *)in;
	r = (LPVF_ReadData_Video)out;

	if(r == NULL){
		return VF_ERROR;
	}
	
	if(r->dwSize != sizeof(VF_ReadData_Video)){
		return VF_ERROR;
	}

	switch(w->config.field_mode){
	case 0: /* keep original frame */
		top = read_frame(w, r->dwFrameNumber);
		bottom = top;
		if(top == NULL){
			__asm{emms};
			return VF_ERROR;
		}
		break;
	case 1: /* top field first */
		top = read_frame(w, r->dwFrameNumber);
		if(top == NULL){
			__asm{emms};
			return VF_ERROR;
		}
		bottom = NULL;
		if( (!top->prm.top_field_first) && (!top->prm.repeat_first_field) ){
			bottom = read_frame(w, r->dwFrameNumber+1);
		}
		if(bottom == NULL){
			bottom = top;
		}

		break;
	case 2: /* bottom field first */
		bottom = read_frame(w, r->dwFrameNumber);
		if(bottom == NULL){
			__asm{emms};
			return VF_ERROR;
		}
		top = NULL;
		if( bottom->prm.top_field_first && (!bottom->prm.repeat_first_field) ){
			top = read_frame(w, r->dwFrameNumber+1);
		}
		if(top == NULL){
			top = bottom;
		}
		break;
	default:
		top = read_frame(w, r->dwFrameNumber);
		bottom = top;
		if(top == NULL){
			__asm{emms};
			return VF_ERROR;
		}
	}
	
	dst = (unsigned char *)r->lpData;
	
	w->bgr_prm.out_step = r->lPitch;
	w->yuv2bgr(top->data, bottom->data, dst, &(w->bgr_prm));


	return VF_OK;
}