//  Copyright (c) 2007-2008 Fredrik Mellbin
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.

#include <string>
#include "ffms.h"
#include "ffavisynth.h"
#include "ffswscale.h"
#include "ffpp.h"
#include "utils.h"

AVSValue __cdecl CreateFFIndex(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
	FFMS_Init();

	char ErrorMsg[1024];
	unsigned MsgSize = sizeof(ErrorMsg);


	if (!Args[0].Defined())
    	Env->ThrowError("FFIndex: No source specified");

	const char *Source = Args[0].AsString();
	const char *CacheFile = Args[1].AsString("");
	int TrackMask = Args[2].AsInt(0);
	const char *AudioFile = Args[3].AsString("");
	bool OverWrite = Args[4].AsBool(false);

	std::string DefaultCache(Source);
	DefaultCache.append(".ffindex");
	if (!strcmp(CacheFile, ""))
		CacheFile = DefaultCache.c_str();

	if (!strcmp(AudioFile, ""))
		AudioFile = Source;

	// Return values
	// 0: Index already present
	// 1: Index generated
	// 2: Index forced to be overwritten

	FrameIndex *Index = FFMS_CreateFrameIndex();
	if (OverWrite || FFMS_ReadIndex(CacheFile, Index, ErrorMsg, MsgSize)) {
		if (FFMS_MakeIndex(Source, Index, TrackMask, AudioFile, NULL, ErrorMsg, MsgSize))
			Env->ThrowError("FFIndex: %s", ErrorMsg);
		if (FFMS_WriteIndex(CacheFile, Index, ErrorMsg, MsgSize))
			Env->ThrowError("FFIndex: %s", ErrorMsg);
		FFMS_DestroyFrameIndex(Index);
		if (!OverWrite)
			return AVSValue(1);
		else
			return AVSValue(2);
	} else {
		FFMS_DestroyFrameIndex(Index);
		return AVSValue(0);
	}
}

AVSValue __cdecl CreateFFVideoSource(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
	FFMS_Init();

	char ErrorMsg[1024];
	unsigned MsgSize = sizeof(ErrorMsg);

	if (!Args[0].Defined())
    	Env->ThrowError("FFVideoSource: No source specified");

	const char *Source = Args[0].AsString();
	int Track = Args[1].AsInt(-1);
	bool Cache = Args[2].AsBool(true);
	const char *CacheFile = Args[3].AsString("");
	const char *PP = Args[4].AsString("");
	int Threads = Args[5].AsInt(-1);
	const char *Timecodes = Args[6].AsString("");
	int SeekMode = Args[7].AsInt(1);

	if (Track <= -2)
		Env->ThrowError("FFVideoSource: No video track selected");

	if (SeekMode < -1 || SeekMode > 3)
		Env->ThrowError("FFVideoSource: Invalid seekmode selected");

	if (Threads <= 0) 
		Threads = GetNumberOfLogicalCPUs();
	if (Threads < 1)
		Env->ThrowError("FFVideoSource: Invalid thread count");

	std::string DefaultCache(Source);
	DefaultCache.append(".ffindex");
	if (!strcmp(CacheFile, ""))
		CacheFile = DefaultCache.c_str();

	FrameIndex *Index = FFMS_CreateFrameIndex();
	if (Cache) {
		if (FFMS_ReadIndex(CacheFile, Index, ErrorMsg, MsgSize)) {
			if (FFMS_MakeIndex(Source, Index, 0, NULL, NULL, ErrorMsg, MsgSize))
				Env->ThrowError("FFVideoSource: %s", ErrorMsg);

			if (Cache)
				if (FFMS_WriteIndex(CacheFile, Index, ErrorMsg, MsgSize))
					Env->ThrowError("FFVideoSource: %s", ErrorMsg);
		}
	}

	AvisynthVideoSource *Filter = new AvisynthVideoSource(Source, Track, Index, PP, Threads, SeekMode, Env, ErrorMsg, MsgSize);

	if (strcmp(Timecodes, ""))
		if (FFMS_WriteTimecodes(FFMS_GetTITrackIndex(Index, Filter->GetTrack(), ErrorMsg, MsgSize), Timecodes, ErrorMsg, MsgSize))
			Env->ThrowError("FFVideoSource: %s", ErrorMsg);

	return Filter;
}

AVSValue __cdecl CreateFFPP(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
	return new FFPP(Args[0].AsClip(), Args[1].AsString(""), Env);
}

AVSValue __cdecl CreateSWScale(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
	return new SWScale(Args[0].AsClip(), Args[1].AsInt(0), Args[2].AsInt(0), Args[3].AsString("BICUBIC"), Args[4].AsString(""), Env);
}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* Env) {
    Env->AddFunction("FFIndex", "[source]s[cachefile]s[trackmask]i[audiofile]s[overwrite]b", CreateFFIndex, 0);
    Env->AddFunction("FFVideoSource", "[source]s[track]i[cache]b[cachefile]s[pp]s[threads]i[timecodes]s[seekmode]i", CreateFFVideoSource, 0);
	Env->AddFunction("FFPP", "c[pp]s", CreateFFPP, 0);
	Env->AddFunction("SWScale", "c[width]i[height]i[resizer]s[colorspace]s", CreateSWScale, 0);

    return "FFmpegSource - The Second Coming";
}