// FrameMap 0.2 + PDecimate 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>

// FrameMap

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

};

FrameMap::FrameMap(int *_frames, int _no_frames, bool setfps, PClip _child, IScriptEnvironment* env)
	: GenericVideoFilter(_child) {
	frames		= _frames;
	no_frames	= _no_frames;
	if(setfps) {
		VideoInfo cvi = _child->GetVideoInfo();
		double n = (((double)cvi.fps_numerator) / ((double)cvi.fps_denominator));
		int d = 1;
		n *= ((double)no_frames) / ((double)vi.num_frames);
		while (n < 16777216 && d < 16777216) { n*=2; d*=2; }
		//d *= int((((double)vi.num_frames) / ((double)no_frames))+0.5);
		vi.SetFPS((unsigned)n,(unsigned)d);
	}
	vi.num_frames	= no_frames;
}

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

PVideoFrame __stdcall FrameMap::GetFrame(int n, IScriptEnvironment* env) {
	return child->GetFrame(frames[n], env);
}

bool __stdcall FrameMap::GetParity(int n) {
	return child->GetParity(frames[n]);
}

// DLL Handles

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

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

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

	for(i = 0; i < no_frames; ++i) {
		int ptr = 0, val = 0;
		
		if(fscanf(fh,"%d\t%d\n",&ptr,&val) < 2 
				|| ptr < 0 || val < 0 
				|| ptr >= no_frames) {
				//|| val >= vi.num_frames) {
			env->ThrowError("FrameMap: invalid map file");
		}
		frames[ptr] = val;
	}
		
	fclose(fh);
	
	return new FrameMap(frames, no_frames, false, args[0].AsClip(), env);
}

AVSValue __cdecl Create_PDecimate(AVSValue args, void*, IScriptEnvironment* env) {
	PClip clip = args[0].AsClip();
	VideoInfo vi = clip->GetVideoInfo(); 
		
	const char *pattern = args[1].AsString("");
	int plength = strlen(pattern);
	if(plength == 0)
		env->ThrowError("PDecimate: invalid pattern (length = 0)");
	
	int no_frames;
	int *frames;
	
	int i, fi, pi, nx = 0, no = 0;

	for(i = 0; i < plength; ++i) {
		switch(tolower(pattern[i])) {
			case 'x': nx++; break;
			case 'o': no++; break;
			default:
				  env->ThrowError("PDecimate: invalid pattern (bad character, must be 'x' or 'o')");
		}
	}
	
	if(nx == 0)
		env->ThrowError("PDecimate: invalid pattern (must have at least 1 'x')");

	frames = new int[((vi.num_frames / plength) * nx) + plength]; // slack in the allocation
	for(fi = 0, pi = 0, no_frames = 0; fi < vi.num_frames; ++fi, ++pi) {
		if(pi >= plength)
			pi = 0;
		if(tolower(pattern[pi]) == 'x')
			frames[no_frames++] = fi;
	}		

	return new FrameMap(frames, no_frames, true, clip, env);
}

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