// BlendMap 0.1
// Copyright 2004 Carl Ritson <critson@perlfu.co.uk>

// 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; either version 2 of the License, or
// (at your option) any later version.
//
// 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, or visit
// http://www.gnu.org/copyleft/gpl.html .

#include "windows.h"
#include "avisynth.h"

#include <stdio.h>

// BlendMap

class BlendMap : public GenericVideoFilter {
	public: 
		BlendMap(bool *_frames, int _no_frames, PClip _child, IScriptEnvironment* env);
		~BlendMap();
		PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env); 
	private:
		bool *frames;
		int no_frames;

};

BlendMap::BlendMap(bool *_frames, int _no_frames, PClip _child, IScriptEnvironment* env)
	: GenericVideoFilter(_child) {
	if (!vi.IsYV12())
		env->ThrowError("BlendMap: requires YV12 source");
	frames		= _frames;
	no_frames	= _no_frames;
	vi.num_frames	= _no_frames;
}

BlendMap::~BlendMap() {
	if(frames)
		delete frames;
}

PVideoFrame __stdcall BlendMap::GetFrame(int n, IScriptEnvironment* env) {
	if(!frames[n] || n == 0 || n >= (vi.num_frames-1))
		return child->GetFrame(n, env);

	PVideoFrame s1Frame	= child->GetFrame(n-1, env);
	PVideoFrame tgFrame	= child->GetFrame(n, env);
	PVideoFrame s2Frame	= child->GetFrame(n+1, env);
	
	env->MakeWritable(&tgFrame);
	
	unsigned char *tgPtr,*s1Ptr,*s2Ptr;
	int row_size,height,h,w;
	
	// Y
	
	tgPtr = (unsigned char *) tgFrame->GetWritePtr(PLANAR_Y);
	s1Ptr = (unsigned char *) s1Frame->GetReadPtr(PLANAR_Y);
	s2Ptr = (unsigned char *) s2Frame->GetReadPtr(PLANAR_Y);
	row_size = tgFrame->GetRowSize(PLANAR_Y);
	height = tgFrame->GetHeight(PLANAR_Y);
	h,w;

	for(h = 0; h < height; ++h) {
		unsigned char *tg = tgPtr + (h * row_size);
		unsigned char *s1 = s1Ptr + (h * row_size);
		unsigned char *s2 = s2Ptr + (h * row_size);
		unsigned char *pEnd = tg + row_size;
		while(tg < pEnd) {
			*tg = (*s1 + *s2) / 2;
			tg++; s1++; s2++;
		}
	}
	
	// U
	
	tgPtr = (unsigned char *) tgFrame->GetWritePtr(PLANAR_U);
	s1Ptr = (unsigned char *) s1Frame->GetReadPtr(PLANAR_U);
	s2Ptr = (unsigned char *) s2Frame->GetReadPtr(PLANAR_U);
	
	row_size = tgFrame->GetRowSize(PLANAR_U);
	height = tgFrame->GetHeight(PLANAR_U);
	
	for(h = 0; h < height; ++h) {
		unsigned char *tg = tgPtr + (h * row_size);
		unsigned char *s1 = s1Ptr + (h * row_size);
		unsigned char *s2 = s2Ptr + (h * row_size);
		unsigned char *pEnd = tg + row_size;
		while(tg < pEnd) {
			*tg = (*s1 + *s2) / 2;
			tg++; s1++; s2++;
		}
	}

	// V
	
	tgPtr = (unsigned char *) tgFrame->GetWritePtr(PLANAR_V);
	s1Ptr = (unsigned char *) s1Frame->GetReadPtr(PLANAR_V);
	s2Ptr = (unsigned char *) s2Frame->GetReadPtr(PLANAR_V);
	
	row_size = tgFrame->GetRowSize(PLANAR_V);
	height = tgFrame->GetHeight(PLANAR_V);
	
	for(h = 0; h < height; ++h) {
		unsigned char *tg = tgPtr + (h * row_size);
		unsigned char *s1 = s1Ptr + (h * row_size);
		unsigned char *s2 = s2Ptr + (h * row_size);
		unsigned char *pEnd = tg + row_size;
		while(tg < pEnd) {
			*tg = (*s1 + *s2) / 2;
			tg++; s1++; s2++;
		}
	}
	
	return tgFrame;
}


// DLL Handles

AVSValue __cdecl Create_BlendMap(AVSValue args, void*, IScriptEnvironment* env) {
	FILE *fh;
	bool *frames;
	int i,no_frames;

	fh = fopen(args[1].AsString(""),"r");
	if(fh == NULL)
		env->ThrowError("BlendMap: unable to open file");

	if(fscanf(fh,"%d\n\n",&no_frames) < 1 || no_frames <= 0)
		env->ThrowError("BlendMap: error reading map file");
	
	frames = new bool[no_frames];
	for(i = 0; i < no_frames; ++i)
		frames[i] = false;

	for(i = 0; i < no_frames; ++i) {
		float val = 0;
		int ptr = 0;
		
		if(fscanf(fh,"%d\t%f\n",&ptr,&val) < 2)
			env->ThrowError("BlendMap: invalid map file");
		if(ptr > 0) {
			if(!frames[ptr-1] && val == 0.0)
				frames[ptr] = true;
		}
	}
		
	fclose(fh);
	
	return new BlendMap(frames, no_frames, args[0].AsClip(), env);
}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env) {
	env->AddFunction("BlendMap", "cs", Create_BlendMap, 0);
	return "BlendMap filter";
}
