/*
   Auto Deinterlacer - Automatically restores PAL movie interlace order
	V3.0 5- May 01 use Donald's code for deinterlacing
	V3.1 alpha1 15-May-01 Assume filter input switches between Case 1 and Case 5
									(insert another filter before this one if necessary)
	V3.1 alpha2 1-Jun-01 Remove hysterisis - shift can change each frame if 
								it wants to.
								Added Delta scene change threshold parameters
	V3.1 alpha3 3-Jun-2001 - Try make it faster!
	V3.1 alpha4 3-Jun-2001 - simplify use of Donald's code.
	V3.1 beta11 4-Jun-2001 - Make suitable for release
									Add field swap option
	V3.3 - comes from V3.1 - 3.2 left alone
			Interpolate 1st frame from top field at normal/poor changeover
			re-add in single frame avs scripting
			remove scene detect code
	V3.4 started
			Added control over number of interpolated frames used after
			switch from normal to perverse processing.
			Output values used for switching as #comments in avs file to 
			help manual editing descisions

   Copyright (C) 2000-2002 Simon Walters 
	
	Based on code by Avery Lee, Jim Casaburi, Gunnar Thalin and Donald Graft.
	Inital impetus to writing filters given by Donald Graft
	This filter derived from Gunnar's PAL DeInterlacer with my code for 
	auto switching and then I added Donald's code for buffering frames.
		

    This program 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.

    This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.

	The author can be contacted at:
	Simon Walters
	siwalters@hotmail.com

	New versions of the source code and the compiled filter can be 
	found at http://www.geocities.com/siwalters_uk/fnews.htm
*/

#include "filter.h"
#include "resource.h"
#include "ScriptError.h"
#include "ScriptInterpreter.h"
#include "ScriptValue.h"
#include "stdio.h"

// Added to use Donald Graft's Dialog Box
#include <windows.h>
#include <commctrl.h>
//#include <crtdbg.h>

#include <string>
#include <fstream>
#include <iostream>

using namespace  std;


///////////////////////
static int RunProc(const FilterActivation *fa, const FilterFunctions *ff);
static int InitProc(FilterActivation *fa, const FilterFunctions *ff);
static int StartProc(FilterActivation *fa, const FilterFunctions *ff);
static int EndProc(FilterActivation *fa, const FilterFunctions *ff);
static long ParamProc(FilterActivation *fa, const FilterFunctions *ff);

static int ConfigProc(FilterActivation *fa, const FilterFunctions *ff, HWND hwnd);
static void StringProc(const FilterActivation *fa, const FilterFunctions *ff, char *buf); 
static bool FssProc(FilterActivation *fa, const FilterFunctions *ff, char *buf, int buflen);
static void ScriptConfig(IScriptInterpreter *isi, void *lpVoid, CScriptValue *argv, int argc); 


typedef struct MyFilterData {
	IFilterPreview		*ifp;  // Something to do with preview
	bool			bShiftStart;
	bool			bShift;
	bool			bShowShift;
//	bool			bShowFieldDiffs;
	signed int	PlusThresh;
	signed int	MinusThresh;
	bool			bGenAvsScript;
	signed int	DeltaThresh;
	signed int	DeltaShiftThresh;
	bool			bDebugView;
	bool			bFieldSwap;
	signed int	MaxInterpolateFrames;
	bool			bShowDiffs;
	Pixel32		*prevFrame,*NormalDiff,*ShiftedDiff;

    } MyFilterData;

static bool FssProc(FilterActivation *fa, const FilterFunctions *ff, char *buf, int buflen) {
	MyFilterData *mfd = (MyFilterData *)fa->filter_data;

	_snprintf(buf, buflen, "Config(%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)",
		mfd->bShiftStart,
		mfd->PlusThresh,
		mfd->MinusThresh,
		mfd->bGenAvsScript,
		mfd->bShowShift,
		mfd->DeltaThresh,
		mfd->DeltaShiftThresh,
		mfd->bDebugView,
		mfd->bFieldSwap,
		mfd->MaxInterpolateFrames,
		mfd->bShowDiffs);

	return true;
}

static void ScriptConfig(IScriptInterpreter *isi, void *lpVoid, CScriptValue *argv, int argc) {
	FilterActivation *fa = (FilterActivation *)lpVoid;
	MyFilterData *mfd = (MyFilterData *)fa->filter_data;

	mfd->bShiftStart			= !!argv[0].asInt();
	mfd->PlusThresh			= argv[1].asInt();
	mfd->MinusThresh			= argv[2].asInt();
	mfd->bGenAvsScript		= !!argv[3].asInt();
	mfd->bShowShift			= !!argv[4].asInt();
	mfd->DeltaThresh			= argv[5].asInt();
	mfd->DeltaShiftThresh	= argv[6].asInt();
	mfd->bShowShift			= !!argv[7].asInt();
	mfd->bFieldSwap			= !!argv[8].asInt();
	mfd->MaxInterpolateFrames = argv[9].asInt();
	mfd->bShowDiffs			= !!argv[10].asInt();

}
ScriptFunctionDef func_defs[]={
	{ (ScriptFunctionPtr)ScriptConfig, "Config", "0iiiiiiiiiii" },
	{ NULL },
};

CScriptObject script_obj={
	NULL, func_defs
};


///////////////////////////////
struct FilterDefinition filterDef_AutoDInt = {

    NULL, NULL, NULL,									// next, prev, module
	#ifdef _DEBUG
		"0 deinterlacer - Auto PAL V3.4 beta1 Dbg",// name
	#else
		"deinterlacer - Auto PAL V3.4 beta1",		// name
	#endif
   "Restores original progressive frames in PAL video",	// desc
   "Simon Walters",										// maker
   NULL,														// private_data
   sizeof(MyFilterData),								// inst_data_size

	InitProc,												// initProc
   NULL,														// deinitProc
   RunProc,													// runProc
   NULL,												// paramProc
	ConfigProc,												// configProc
   StringProc,												// stringProc
   StartProc,														// startProc
   EndProc,														// endProc

   &script_obj,											// &script_obj
	FssProc,													// fssProc


};


///////////////////////////////////////////////////////////////


extern "C" int __declspec(dllexport) __cdecl VirtualdubFilterModuleInit2(FilterModule *fm, const FilterFunctions *ff, int& vdfd_ver, int& vdfd_compat);
extern "C" void __declspec(dllexport) __cdecl VirtualdubFilterModuleDeinit(FilterModule *fm, const FilterFunctions *ff);

static FilterDefinition *fd_AutoDInt;
////////////////////////////////////////////////////////////

int __declspec(dllexport) __cdecl VirtualdubFilterModuleInit2(FilterModule *fm, const FilterFunctions *ff, int& vdfd_ver, int& vdfd_compat) {
    if (!(fd_AutoDInt = ff->addFilter(fm, &filterDef_AutoDInt, sizeof(FilterDefinition))))
        return 1;

    vdfd_ver    = VIRTUALDUB_FILTERDEF_VERSION;
    vdfd_compat = VIRTUALDUB_FILTERDEF_COMPATIBLE;

    return 0;
}

void __declspec(dllexport) __cdecl VirtualdubFilterModuleDeinit(FilterModule *fm, const FilterFunctions *ff) {
    ff->removeFilter(fd_AutoDInt);
}

////
static int StartProc(FilterActivation *fa, const FilterFunctions *ff) {
	MyFilterData *mfd = (MyFilterData *)fa->filter_data;

	mfd->prevFrame		= new Pixel32[fa->src.w*fa->src.h];
	memset(mfd->prevFrame, 0, fa->src.w*fa->src.h*sizeof(Pixel32));

	mfd->NormalDiff		= new Pixel32[fa->src.w*fa->src.h];
	memset(mfd->NormalDiff, 0, fa->src.w*fa->src.h*sizeof(Pixel32));

	mfd->ShiftedDiff		= new Pixel32[fa->src.w*fa->src.h];
	memset(mfd->ShiftedDiff, 0, fa->src.w*fa->src.h*sizeof(Pixel32));

	return 0;
}

static int EndProc(FilterActivation *fa, const FilterFunctions *ff)
{
	MyFilterData *mfd = (MyFilterData *)fa->filter_data;

	delete[] mfd->prevFrame;
	mfd->prevFrame = NULL;

	delete[] mfd->NormalDiff;
	mfd->NormalDiff = NULL;

	delete[] mfd->ShiftedDiff;
	mfd->ShiftedDiff = NULL;

	return 0;
}


//////////////////////////////////////
static int RunProc(const FilterActivation *fa, const FilterFunctions *ff) {

   MyFilterData *mfd = (MyFilterData *)fa->filter_data;
   PixDim w, h;
   Pixel32 *src, *dst;

	static int LastDiff = 0;

	static int lastshiftframe = 0;
	static bool lastshift = mfd->bShiftStart;
	
	static int LastTotDiff = 1;
	static int LastTotDiffShift = 1;
	static int LastTrig = -1;
	static int SceneChangeFrame = 0;
	static int FramesSinceChangetoShift = 0;

	int TotDiff =1;
//	int NormPicDetail = 0;
	Pixel32 PixelDiff;
	int LSBMask = 0xFFC0;

/*
0001 == 1
0010 == 2
0100 == 4
1000 == 8
1100 == 12 == C
1110 == 14 == E
1111 == 15 == F
*/

	ofstream out("TotDiffDev.txt", ios::app);


	ofstream avs("avs.avs", ios::app);


	Pixel32 current,minus1,minus2;
	int srcpitch = fa->src.pitch;
	int dstpitch = fa->dst.pitch;
	int bufferlinepitch = fa->dst.w;
	const int cropvalue = 20;
	srcpitch = srcpitch >> 2;
	dstpitch = dstpitch >> 2;

	// copy src to dst
	if (mfd->bFieldSwap == TRUE) {
		// copy top source to bottom dst
		src = (Pixel32 *)((char *)fa->src.data + fa->src.pitch );
		dst = (Pixel32 *)fa->dst.data;
		h = fa->src.h >> 1;
		do {
			memcpy(dst, src, fa->src.w * sizeof(Pixel32));
			src = (Pixel32 *)((char *)src + (fa->src.pitch << 1));
			dst = (Pixel32 *)((char *)dst + (fa->dst.pitch << 1));
		} while(--h);

		// copy bottom source to top dst
		src = (Pixel32 *)fa->src.data;
		dst = (Pixel32 *)((char *)fa->dst.data + fa->dst.pitch );
		h = fa->src.h >> 1;
		do {
			memcpy(dst, src, fa->src.w * sizeof(Pixel32));
			src = (Pixel32 *)((char *)src + (fa->src.pitch << 1));
			dst = (Pixel32 *)((char *)dst + (fa->dst.pitch << 1));
		} while(--h);
	}
	else {
		// straight copy src to dst
		src = (Pixel32 *)fa->src.data;
		dst = (Pixel32 *)fa->dst.data;
		h = fa->src.h;
		do {
			memcpy(dst, src, fa->src.w * sizeof(Pixel32));
			src = (Pixel32 *)((char *)src + fa->src.pitch );
			dst = (Pixel32 *)((char *)dst + fa->dst.pitch);
		} while(--h);
	}
/////////////////////////////////////////////////////////////////

//Scan src data for differences between adjacent lines
   h = fa->dst.h - cropvalue;
	do {
		int dstpitchh	= dstpitch*h;
		int dstpitchh1	= dstpitch*(h+1);
		int dstpitchh2	= dstpitch*(h+2);
		int bufferpitch = bufferlinepitch*h;
		w = fa->dst.w;
      do {
			current = *((Pixel32 *)fa->dst.data + (dstpitchh)	+ w);
			minus1 = *((Pixel32 *)fa->dst.data	+ (dstpitchh1) + w);
			minus2 = *((Pixel32 *)fa->dst.data	+ (dstpitchh2) + w);
			int currenttot = ((current & 0xFF0000) >> 16) + ((current & 0xFF00) >> 8) + (current & 0xFF);
			int minus1tot = ((minus1 & 0xFF0000) >> 16) + ((minus1 & 0xFF00) >> 8) + (minus1 & 0xFF);
			int minus2tot = ((minus2 & 0xFF0000) >> 16) + ((minus2 & 0xFF00) >> 8) + (minus2 & 0xFF);
			PixelDiff = abs(((currenttot + minus2tot) >> 1) - minus1tot) & LSBMask;
			*((Pixel32 *)mfd->NormalDiff + bufferpitch	+ w) = PixelDiff;
			TotDiff	+= PixelDiff;
		} while(--w);
		h = h - 2;
   } while(h > cropvalue);
/////////////////////////////////////////////////////////////

// Do equivalent of a swap followed by a phase shift
	
	Pixel32 *saved;
	int y;

	//write bottom field from buffer to dst
   dst = (Pixel32 *)fa->dst.data;
	saved = mfd->prevFrame;
	h = fa->dst.h;
	for (y = 0; y < h; y += 2)
	{					
		memcpy(dst, saved, fa->dst.w * sizeof(Pixel32));
		dst = (Pixel32 *)((char *)dst + (fa->dst.pitch << 1));
		saved += (fa->dst.w << 1);
	}
//////////////////////////////////////////////////////////////////

	// do new check on whether source should be considered Type4
	int TotDiffShift = 1;
	int ShiftPicDetail = 0;

   h = fa->dst.h - cropvalue;
	do {
		w = fa->dst.w;
		int dstpitchh	= dstpitch*h;
		int dstpitchh1	= dstpitch*(h+1);
		int dstpitchh2	= dstpitch*(h+2);
		int bufferpitch = bufferlinepitch*h;
      do {
			current = *((Pixel32 *)fa->dst.data + (dstpitchh) + w);
			minus1 = *((Pixel32 *)fa->dst.data + (dstpitchh1) + w);
			minus2 = *((Pixel32 *)fa->dst.data + (dstpitchh2) + w);
			int currenttot = ((current & 0xFF0000) >> 16) + ((current & 0xFF00) >> 8) + (current & 0xFF);
			int minus1tot = ((minus1 & 0xFF0000) >> 16) + ((minus1 & 0xFF00) >> 8) + (minus1 & 0xFF);
			int minus2tot = ((minus2 & 0xFF0000) >> 16) + ((minus2 & 0xFF00) >> 8) + (minus2 & 0xFF);
			PixelDiff = abs(((currenttot + minus2tot) >> 1) - minus1tot) & LSBMask;
			*((Pixel32 *)mfd->ShiftedDiff + bufferpitch	+ w) = PixelDiff;
			TotDiffShift	+= PixelDiff;
		} while(--w);
		h = h - 2;
   } while(h > cropvalue);
////////////////////////////////////////////////////////////////////////////
// Do a diff check between prev top and bottom (just for fun)
/*

	int TotDiffPrev = 1;
	int PrevPicDetail = 0;
	saved = mfd->prevFrame;
   h = fa->dst.h - cropvalue;
	do {
		w = fa->dst.w;
		int dstpitchh	= w*h;
		int dstpitchh1	= w*(h+1);
		int dstpitchh2	= w*(h+2);
      do {
			current = *(saved + dstpitchh + w);
			minus1 = *(saved + dstpitchh1 + w);
			minus2 = *(saved + dstpitchh2 + w);
			int currenttot = ((current & 0xFF0000) >> 16) + ((current & 0xFF00) >> 8) + (current & 0xFF);
			int minus1tot = ((minus1 & 0xFF0000) >> 16) + ((minus1 & 0xFF00) >> 8) + (minus1 & 0xFF);
			int minus2tot = ((minus2 & 0xFF0000) >> 16) + ((minus2 & 0xFF00) >> 8) + (minus2 & 0xFF);
//			minus2tot = currenttot;
			TotDiffPrev	+= abs(((currenttot + minus2tot) >> 1) - minus1tot) & LSBMask;
			PrevPicDetail += abs(currenttot - minus2tot);
//			if (mfd->bShowFieldDiffs)
//				*(dst)=pixeldiff;
		} while(--w);
		h = h - 2;
   } while(h > cropvalue);
// end of prev bottom top check
*/
////////////////////////////////////////////////////////////////////////////
// Do a diff check between top? and prev top (just for fun)

/*
	int TotDiffPrevTT = 1;
	saved = mfd->prevFrame;
   h = fa->dst.h - cropvalue;
	do {
		w = fa->dst.w;
		int dstpitchh	= dstpitch*h;
		int dstpitchh1	= w*h;
      do {
			current = *((Pixel32 *)fa->src.data + (dstpitchh) + w);
			prevline = *(saved + dstpitchh1 + w);
			int currenttot = ((current & 0xFF0000) >> 16) + ((current & 0xFF00) >> 8) + (current & 0xFF);
			int prevlinetot = ((prevline & 0xFF0000) >> 16) + ((prevline & 0xFF00) >> 8) + (prevline & 0xFF);
			TotDiffPrevTT	+= abs(currenttot  - prevlinetot) & LSBMask;
		} while(--w);
		h = h - 2;
   } while(h > cropvalue);
// end of top v prev top check
*/
/*
///// Remove points that appear in both differnence frames
		// write normal diffs
		dst = (Pixel32 *)((char *)fa->dst.data);
		Pixel32 *NormalDiffBuffer = mfd->NormalDiff;
		Pixel32 *ShiftedDiffBuffer = mfd->ShiftedDiff;
		TotDiff = 0;
		TotDiffShift = 0;
		h = fa->dst.h;
		for (y = 0; y < (h); y +=1) {
			for (w=0; w < fa->dst.w; w++) {
// discard unimportamt info
//				if ((*NormalDiffBuffer > 0) && (*ShiftedDiffBuffer>0)) {
//					*NormalDiffBuffer = 0;
//					*ShiftedDiffBuffer = 0;
//				}
				// use diff between the two differnence buffers
				if (*NormalDiffBuffer > *ShiftedDiffBuffer) {
					*NormalDiffBuffer -=	*ShiftedDiffBuffer;
					*ShiftedDiffBuffer = 0;
				}
				else {
					*ShiftedDiffBuffer -= *NormalDiffBuffer;
					*NormalDiffBuffer =	0;
				}

				TotDiff	+= *NormalDiffBuffer;
				TotDiffShift	+= *ShiftedDiffBuffer;
				dst++;
				NormalDiffBuffer++;
				ShiftedDiffBuffer++;
			}
			dst = (Pixel32 *)((char *)dst + (fa->dst.modulo));
		}
///// end remove points
*/	
//Make descision as to which one to use for output clip
	// work out diff between treating as Type3 or ype 4
	TotDiff += 1;
	TotDiffShift += 1;
	int ThisDiff = TotDiff-TotDiffShift;
	float Ratio = 0;

	if (TotDiff > TotDiffShift) {
			Ratio = ((TotDiff*100)/TotDiffShift)-100;
	}
	else {
		Ratio = ((TotDiffShift*100)/TotDiff)-100;
		Ratio = Ratio * -1;
	}
	//////////////////////////////////////////// end work out diff ratio

	//work out delta between last frame and this frame

	if (fa->pfsi->lCurrentFrame == 0)	{
		mfd->bShift = mfd->bShiftStart;
	}
////////////////////////////////////////////////////////////////

// CODE TO WORK OUT IF STATE SHOULD BE CHANGED BETWEEN scenes

	if (fa->pfsi->lCurrentFrame != 0){
		if ((Ratio > mfd->PlusThresh) || (Ratio < (-1 * mfd->MinusThresh))){
			if (ThisDiff > 0) {
				mfd->bShift = TRUE;
			}
			else {
				mfd->bShift = FALSE;
			}
		}
	}
	else
	{
		mfd->bShift = mfd->bShiftStart;
	}
	if (mfd->bShift == TRUE) {
		if (lastshift == FALSE) {
			FramesSinceChangetoShift = 1;
		}
	}

/////////////////////////////////////////////

// generate avs file

	if ((fa->pfsi->lCurrentFrame == 0) && mfd->bGenAvsScript) {
		// write out header stuff for avs script
		avs << "normal=AVISource(" << "\"" << "xxxxx.avi" << "\"" << ")" << "\n\n";
		avs << "#framenum=ShowFrameNumber(normal)\n";
		avs << "#framenum=Crop(framenum,0,0,480,30)\n\n";

		avs << "#normal=Crop(normal,0,30,480,546)\n";
		avs << "#normal=StackVertical(framenum,normal)\n\n";

		avs << "perverse=ComplementParity(normal)\n";
		avs << "perverse=SeparateFields(perverse)\n\n";

		avs << "top=SelectEven(perverse)\n";
		avs << "bottom=SelectOdd(perverse)\n\n";

		avs << "bigtop=BicubicResize(top,480,576)\n";
		avs << "bigbottom=BicubicResize(bottom,480,576)\n";
		avs << "prevbb=DuplicateFrame(bigbottom,0)\n\n";

		avs << "perverse=DuplicateFrame(perverse,0)\n";
		avs << "perverse=Weave(perverse)\n\n";

		avs << "#perverse=SubTitle(perverse,\"Perverse\")\n";
		avs << "#bigtop=SubTitle(bigtop,\"Bigtop\")\n\n";

		avs << "#normal=ShowFrameNumber(normal)\n";
		avs << "#perverse=ShowFrameNumber(perverse)\n\n\n";
		avs << "last=Trim(normal,0,0)\n";
	}

// generate avs file (line by line
	if ((fa->pfsi->lCurrentFrame > 0) && mfd->bGenAvsScript) {

		avs << "last=last+Trim(";
		if (mfd->bShift == FALSE) {
			avs << "normal,";
		}
		else {
			if (FramesSinceChangetoShift > 0) {
				avs << "bigtop,";
			}
			else {
				avs << "perverse,";
			}
		}
		avs << (fa->pfsi->lCurrentFrame) << "," << (fa->pfsi->lCurrentFrame ) << ")";
		avs << "# " << Ratio << " " << TotDiff << " " << TotDiffShift;
		avs << "\n";
	}
//////////////////////////////////////////////////////////////


// use original frame if swap not needed.
//	but only need the bottom field as top is the same in each case
	
	if (mfd->bShift == FALSE) {
		if (mfd->bFieldSwap == TRUE) {
		// copy top source(which is really the bottom field) to bottom dst
			src = (Pixel32 *)((char *)fa->src.data + fa->src.pitch);
			dst = (Pixel32 *)fa->dst.data;
			h = fa->src.h >> 1;
			do {
				memcpy(dst, src, fa->src.w * sizeof(Pixel32));
				src = (Pixel32 *)((char *)src + (fa->src.pitch << 1));
				dst = (Pixel32 *)((char *)dst + (fa->dst.pitch << 1));
			} while(--h);
		}
		else {
		// copy bottom source to bottom dst
			src = (Pixel32 *)fa->src.data;
			dst = (Pixel32 *)fa->dst.data;
			h = fa->src.h >> 1;
			do {
				memcpy(dst, src, fa->src.w * sizeof(Pixel32));
				src = (Pixel32 *)((char *)src + (fa->src.pitch << 1));
				dst = (Pixel32 *)((char *)dst + (fa->dst.pitch << 1));
			} while(--h);
		}		
	}
// Special changeover frame - use top to generate bottom field
	if ((mfd->bShift == TRUE) && (FramesSinceChangetoShift > 0)) {
		// put black into bottom dst
/*		dst = (Pixel32 *)fa->dst.data;
		h = fa->src.h >> 1;
		do {
			memset(dst, 0, fa->src.w * sizeof(Pixel32));
			dst = (Pixel32 *)((char *)dst + (fa->dst.pitch << 1));
		} while(--h); // end put black into bottom field
*/
/*
		// copy top field to bottom field of output frame
			Pixel32 *bottomdst, *topdst;
			topdst = (Pixel32 *)((char *)fa->dst.data + fa->dst.pitch);
			bottomdst = (Pixel32 *)fa->dst.data;
			h = fa->dst.h >> 1;
			do {
				memcpy(bottomdst, topdst, fa->dst.w * sizeof(Pixel32));
				topdst = (Pixel32 *)((char *)topdst + (fa->dst.pitch << 1));
				bottomdst = (Pixel32 *)((char *)bottomdst + (fa->dst.pitch << 1));
			} while(--h); //
*/
			// simple interpolate bottom from top
			int top1,top2;
			h = 1;
			do {
				int top1pitchh	= dstpitch*h;
				int bottompitchh	= dstpitch*(h+1);
				int top2pitchh	= dstpitch*(h+2);
				w = fa->dst.w;
				do {
					top1 = *((Pixel32 *)fa->dst.data + top1pitchh	+ w);
					top2 = *((Pixel32 *)fa->dst.data + top2pitchh	+ w);
					int Rbottom = (((top1 & 0xFF0000) >> 16) + ((top2 & 0xFF0000) >> 16)) >> 1;
					int Gbottom = (((top1 & 0x00FF00) >> 8) + ((top2 & 0x00FF00) >> 8)) >> 1;//			minus2tot = currenttot;
					int Bbottom = (((top1 & 0x0000FF)) + ((top2 & 0x0000FF))) >> 1;
					*((Pixel32 *)fa->dst.data	+ bottompitchh + w) = (Rbottom << 16) +(Gbottom << 8) + Bbottom;
					
				} while(--w);
				h = h + 2;
			} while(h < (fa->src.h-3)); // end interpolate bottom from top


	}
////////////////////////////////////////////////////////////////////

// Store src frame in buffer for next pass
	if (mfd->bFieldSwap == TRUE) {
		// get top from source (really the bottom field) and store in buffer
		src = (Pixel32 *)((char *)fa->src.data + fa->src.pitch);
		saved = mfd->prevFrame;
		h = fa->src.h;
		for (y = 0; y < h; y +=2) {					
			memcpy(saved, src, fa->src.w * sizeof(Pixel32));
			src = (Pixel32 *)((char *)src + (fa->src.pitch << 1));
			saved += (fa->src.w << 1);
		}
		// get bottom field from source(really the top) and store in buffer
		src = (Pixel32 *)fa->src.data;
		saved = mfd->prevFrame;
		saved += fa->src.w;
		h = fa->src.h;
		for (y = 0; y < h; y +=2) {					
			memcpy(saved, src, fa->src.w * sizeof(Pixel32));
			src = (Pixel32 *)((char *)src + (fa->src.pitch << 1));
			saved += (fa->src.w << 1);
		}
	}
	else {
		// get top from source and store in buffer
		src = (Pixel32 *)((char *)fa->src.data + fa->src.pitch);
		saved = mfd->prevFrame;
		saved += fa->src.w;
		h = fa->src.h;
		for (y = 0; y < h; y +=2) {					
			memcpy(saved, src, fa->src.w * sizeof(Pixel32));
			src = (Pixel32 *)((char *)src + (fa->src.pitch << 1));
			saved += (fa->src.w << 1);
		}
		// get bottom field from source and store in buffer
		src = (Pixel32 *)fa->src.data;
		saved = mfd->prevFrame;
		h = fa->src.h;
		for (y = 0; y < h; y +=2) {					
			memcpy(saved, src, fa->src.w * sizeof(Pixel32));
			src = (Pixel32 *)((char *)src + (fa->src.pitch << 1));
			saved += (fa->src.w << 1);
		}
	}
////////////////////////////////////////////////////////////////////////////////////
// Write diffs to output frame if required
	if (mfd->bShowDiffs) {
		// write normal diffs
		dst = (Pixel32 *)((char *)fa->dst.data);
		Pixel32 *NormalDiffBuffer = mfd->NormalDiff;
		Pixel32 *ShiftedDiffBuffer = mfd->ShiftedDiff;
		h = fa->dst.h;
		for (y = 0; y < (h); y +=1) {
			for (w=0; w < fa->dst.w; w++) {
//				*dst++ = *NormalDiffBuffer++;
				*dst = ((*NormalDiffBuffer) | (*ShiftedDiffBuffer << 16) | (*ShiftedDiffBuffer << 8));
				if ((*NormalDiffBuffer > 0) && (*ShiftedDiffBuffer>0)) {
					*dst = 0x808080;
				}
				dst++;
				NormalDiffBuffer++;
				ShiftedDiffBuffer++;

			}
//			memcpy(dst, NormalDiffBuffer, fa->dst.w * sizeof(Pixel32));
//			dst = (Pixel32 *)((char *)dst + (fa->dst.pitch));
//			NormalDiffBuffer += (fa->dst.w);
			dst = (Pixel32 *)((char *)dst + (fa->dst.modulo));
		}
/*
		// write shifted diffs
		dst = (Pixel32 *)((char *)fa->dst.data);
		Pixel32 *ShiftedDiffBuffer = mfd->ShiftedDiff;
		h = fa->dst.h;
		dst = (Pixel32 *)((char *)dst + (fa->dst.pitch)*(h>>1));
		ShiftedDiffBuffer += (fa->dst.w)*(h>>1);
		for (y = (h>>1); y < h; y +=1) {					
			memcpy(dst, ShiftedDiffBuffer, fa->dst.w * sizeof(Pixel32));
			dst = (Pixel32 *)((char *)dst + (fa->dst.pitch));
			ShiftedDiffBuffer += (fa->dst.w);
		}
*/
	}





/////////////////////////////////////////////////////////////////////////////////////
// write lines if required
	if (mfd->bShowShift)
	{
		dst = ((Pixel32 *)fa->dst.data + ((fa->dst.pitch/4)*(fa->dst.h-(1))));
//		memset( dst, 255, 256 );
		w = fa->dst.w;
		do {
			if (mfd->bShift == TRUE)
				if (FramesSinceChangetoShift > 0) {
					*dst++ = 0xFF8000;//Orange 
				}
				else {
					*dst++ = 0xFFFF00;//Yellow
				}
			else
				*dst++ = 0xFF;//Blue 
		} while(--w);
	}	

	if (mfd->bDebugView == TRUE)
	{
		if (fa->pfsi->lCurrentFrame == 0)
		{
			out << "\n" << "Frame\t" << "TDff\t" << "TDffShft\t";
			out << "bShft\t";
			out << "ThsDiff\t";
			out << "Ratio\t" ;
			out  << "\n";			
		}
		out << fa->pfsi->lCurrentFrame << "\t" << TotDiff << "\t" << TotDiffShift << "\t";
		out << mfd->bShift << "\t";
		out << ThisDiff  << "\t";
		out << Ratio << "\t" ;
		out  << "\n";

		char buff[200];
		sprintf(buff, "%d %d %d %d %d %d\n", fa->pfsi->lCurrentFrame ,TotDiff , TotDiffShift ,
				mfd->bShift, ThisDiff, (int)Ratio);
		OutputDebugString(buff);
	}


	LastTotDiff = TotDiff;
	LastTotDiffShift = TotDiffShift;
///// note change of Shift state

	if (lastshift != mfd->bShift) 	{
		lastshift = mfd->bShift;
		lastshiftframe = fa->pfsi->lCurrentFrame;
	}

	if (FramesSinceChangetoShift > 0) {
		FramesSinceChangetoShift++;
		if (FramesSinceChangetoShift > mfd->MaxInterpolateFrames) {
			FramesSinceChangetoShift = 0;
		}
	}
	return 0;
}



int InitProc(FilterActivation *fa, const FilterFunctions *ff) {
    MyFilterData *mfd = (MyFilterData *)fa->filter_data;

	mfd->bShiftStart	= false;
	mfd->bShift			= false;
	#ifdef _DEBUG
		mfd->bShowShift	= true;
		mfd->bDebugView	= true;
		mfd->bGenAvsScript = true;
		mfd->bShowDiffs = true;
	#else
		mfd->bShowShift	= false;
		mfd->bDebugView	= false;
		mfd->bGenAvsScript = false;
		mfd->bShowDiffs = false;
	#endif
	mfd->PlusThresh = 500;
	mfd->MinusThresh = 50;
	mfd->DeltaThresh = 1200;
	mfd->DeltaShiftThresh = 1200;
	mfd->bFieldSwap		= false;
	mfd->MaxInterpolateFrames = 1;
	return 0;
}


static long ParamProc(FilterActivation *fa, const FilterFunctions *ff) {
    return FILTERPARAM_NEEDS_LAST | FILTERPARAM_SWAP_BUFFERS;
}


BOOL CALLBACK ConfigDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) {
    MyFilterData *mfd = (MyFilterData *)GetWindowLong(hdlg, DWL_USER);
		
	 HWND hWnd;
			switch(msg) {
        case WM_INITDIALOG:
            SetWindowLong(hdlg, DWL_USER, lParam);
            mfd = (MyFilterData *)lParam;

            CheckDlgButton(hdlg, IDC_SHIFTSTART, mfd->bShiftStart?BST_CHECKED:BST_UNCHECKED);
            CheckDlgButton(hdlg, IDC_SHOWSHIFT, mfd->bShowShift?BST_CHECKED:BST_UNCHECKED);
            CheckDlgButton(hdlg, IDC_CHECK5, mfd->bShowDiffs?BST_CHECKED:BST_UNCHECKED);
				CheckDlgButton(hdlg, IDC_GENAVSSCRIPT, mfd->bGenAvsScript?BST_CHECKED:BST_UNCHECKED);
				CheckDlgButton(hdlg, IDC_CHECK3, mfd->bDebugView?BST_CHECKED:BST_UNCHECKED);
				CheckDlgButton(hdlg, IDC_CHECK4, mfd->bFieldSwap?BST_CHECKED:BST_UNCHECKED);

				
				SetDlgItemInt(hdlg, IDC_EDIT_PLUSTHRESH, mfd->PlusThresh, FALSE);
				hWnd = GetDlgItem(hdlg, IDC_SPIN_PLUSTHRESH);
				SendMessage(hWnd, UDM_SETRANGE, (WPARAM)TRUE, MAKELONG(500,1));
				SendMessage(hWnd, UDM_SETPOS, (WPARAM)TRUE, mfd->PlusThresh);
	
				SetDlgItemInt(hdlg, IDC_EDIT_MINUSTHRESH, mfd->MinusThresh, FALSE);
				hWnd = GetDlgItem(hdlg, IDC_SPIN_MINUSTHRESH);
				SendMessage(hWnd, UDM_SETRANGE, (WPARAM)TRUE, MAKELONG(500,1));
				SendMessage(hWnd, UDM_SETPOS, (WPARAM)TRUE, mfd->MinusThresh);

				SetDlgItemInt(hdlg, IDC_EDIT5, mfd->MaxInterpolateFrames, FALSE);
				hWnd = GetDlgItem(hdlg, IDC_SPIN5);
				SendMessage(hWnd, UDM_SETRANGE, (WPARAM)TRUE, MAKELONG(255,1));
				SendMessage(hWnd, UDM_SETPOS, (WPARAM)TRUE, mfd->MaxInterpolateFrames);




            return TRUE;

			case WM_VSCROLL:
				if ((HWND) lParam == GetDlgItem(hdlg, IDC_SPIN_PLUSTHRESH))
				{
					int PlusThresh = SendMessage(GetDlgItem(hdlg, IDC_SPIN_PLUSTHRESH), UDM_GETPOS, 0, 0);
					if (PlusThresh != mfd->PlusThresh)
					{
						mfd->PlusThresh = PlusThresh;
						SendMessage(GetDlgItem(hdlg, IDC_EDIT_PLUSTHRESH), TBM_SETPOS, (WPARAM)TRUE, (mfd->PlusThresh));
					}
				}
				else if ((HWND) lParam == GetDlgItem(hdlg, IDC_SPIN_MINUSTHRESH))
				{
					int MinusThresh = SendMessage(GetDlgItem(hdlg, IDC_SPIN_MINUSTHRESH), UDM_GETPOS, 0, 0);
					if (MinusThresh != mfd->MinusThresh)
					{
						mfd->MinusThresh = MinusThresh;
						SendMessage(GetDlgItem(hdlg, IDC_EDIT_MINUSTHRESH), TBM_SETPOS, (WPARAM)TRUE, (mfd->MinusThresh));
					}
				}
				// SPIN5/EDIT5 are MaxInterpolateFrames settings
				else if ((HWND) lParam == GetDlgItem(hdlg, IDC_SPIN5))
				{
					int MaxInterpolateFrames = SendMessage(GetDlgItem(hdlg, IDC_SPIN5), UDM_GETPOS, 0, 0);
					if (MaxInterpolateFrames != mfd->MaxInterpolateFrames)
					{
						mfd->MaxInterpolateFrames = MaxInterpolateFrames;
						SendMessage(GetDlgItem(hdlg, IDC_EDIT5), TBM_SETPOS, (WPARAM)TRUE, (mfd->MaxInterpolateFrames));
					}
				}

			break;
        case WM_COMMAND:
            switch(LOWORD(wParam)) {

            case IDOK:
               mfd->bShiftStart = !!IsDlgButtonChecked(hdlg, IDC_SHIFTSTART);
               mfd->bShift = !!IsDlgButtonChecked(hdlg, IDC_SHIFTSTART);
               mfd->bShowShift = !!IsDlgButtonChecked(hdlg, IDC_SHOWSHIFT);
               mfd->bShowDiffs = !!IsDlgButtonChecked(hdlg, IDC_CHECK5);
					mfd->bGenAvsScript = !!IsDlgButtonChecked(hdlg, IDC_GENAVSSCRIPT);
					mfd->bDebugView = !!IsDlgButtonChecked(hdlg, IDC_CHECK3);
					mfd->bFieldSwap = !!IsDlgButtonChecked(hdlg, IDC_CHECK4);
					EndDialog(hdlg, 0);
					return TRUE;
				case IDHELP:
					char prog[256];
					char path[256];
					LPTSTR ptr;
					GetModuleFileName(NULL, prog, 255);
					GetFullPathName(prog, 255, path, &ptr);
					*ptr = 0;
					strcat(path, "plugins\\audint.txt");
					OutputDebugString(path);
					OutputDebugString("\n");
					strcpy(prog, "Notepad ");
					strcat(prog, path);
					WinExec(prog, SW_SHOW);
					return TRUE;
            case IDCANCEL:
                EndDialog(hdlg, 1);
                return FALSE;
            }
            break;
    }

    return FALSE;
}

static int ConfigProc(FilterActivation *fa, const FilterFunctions *ff, HWND hwnd) {
    return DialogBoxParam(fa->filter->module->hInstModule,
            MAKEINTRESOURCE(IDD_FILTER), hwnd,
            (DLGPROC)ConfigDlgProc, (LPARAM)fa->filter_data);
}

static void StringProc(const FilterActivation *fa, const FilterFunctions *ff, char *str) {
   MyFilterData *mfd = (MyFilterData *)fa->filter_data;
	if(((MyFilterData *)(fa->filter_data))->bShift)
		strcpy(str, "(");
	else
		strcpy(str, "(");
	if(((MyFilterData *)(fa->filter_data))->bShowShift)
		strcat(str, ",& show)");
	else
		strcat(str, ")");

}
