#include <loader.h>  // loader has to come first - some compilers have problems here
#include <plugin.h>
#include <default.h>
#include <registry.h>
#include <except.h>

#include "audiodec/acm.h"
#include "audiodec/dshow.h"
#include "videocodec/VideoDecoder.h"
#include "videocodec/DS_VideoDecoder.h"
#include "videocodec/VideoEncoder.h"
#include "DirectShow/guids.h"
#include "fillplugins.h"

#include <cctype>
#include <cstdlib>
#include <cstdio>

using namespace std;

extern "C" char* def_path;

static vector<CodecInfo> libwin32_codecs;
static const char* keyname = "SOFTWARE\\Microsoft\\Scrunch\\Video";
static string last_error;

extern "C" const vector<CodecInfo>& RegisterPlugin()
{
    if (libwin32_codecs.size())
	return libwin32_codecs;
    if (getenv("WIN32_PATH"))
	def_path = getenv("WIN32_PATH");
    libwin32_FillPlugins(libwin32_codecs);

    return libwin32_codecs;
}

extern "C" int GetPluginVersion()
{
    return PLUGIN_API_VERSION;
}

extern "C" IVideoEncoder* CreateVideoEncoder(const CodecInfo& info, fourcc_t compressor, const BITMAPINFOHEADER& bh)
{
    try
    {
	if (!libwin32_codecs.size())
	    RegisterPlugin();

	return new VideoEncoder(info, compressor, bh);
    }
    catch(FatalError& e)
    {
	last_error = e.GetDesc();
	e.Print();
	return 0;
    }
}

extern "C" IVideoDecoder* CreateVideoDecoder(const CodecInfo& info, const BITMAPINFOHEADER& bh, int flip)
{
    try
    {
	if (!libwin32_codecs.size())
	    RegisterPlugin();

	if (info.kind == CodecInfo::DShow_Dec)
	    return new DS_VideoDecoder(info, bh, flip);

	return new VideoDecoder(info, bh, flip);
    }
    catch(FatalError& e)
    {
	last_error = e.GetDesc();
	e.Print();
	return 0;
    }
}

extern "C" IAudioDecoder* CreateAudioDecoder(const CodecInfo& info, WAVEFORMATEX* fmt)
{
    try
    {
	if (!libwin32_codecs.size())
	    RegisterPlugin();

	if (info.kind != CodecInfo::Win32)
	    return new DS_Decoder(info, fmt);

	return new ACM_Decoder(info, fmt);
    }
    catch (FatalError& e)
    {
	last_error=e.GetDesc();
	e.Print();
	return 0;
    }
}

extern "C" string GetError()
{
    return last_error;
}
/****

Here we've got attribute setting functions

****/
#define HKEY_CLASSES_ROOT       (0x80000000)
#define HKEY_CURRENT_USER       (0x80000001)
#define HKEY_LOCAL_MACHINE      (0x80000002)
#define HKEY_USERS              (0x80000003)
#define HKEY_PERFORMANCE_DATA   (0x80000004)
#define HKEY_CURRENT_CONFIG     (0x80000005)
#define HKEY_DYN_DATA           (0x80000006)
#define REG_DWORD		4	/* DWORD in little endian format */

static char* GetKeyName(char* s, fourcc_t fcc)
{
    static const char b[] = "Software\\LinuxLoader\\";
    int i = sizeof(b) - 1;

    strcpy(s, b);
    s[i++] = tolower(fcc & 0xff);
    fcc >>=8;
    s[i++] = tolower(fcc & 0xff);
    fcc >>=8;
    s[i++] = tolower(fcc & 0xff);
    fcc >>=8;
    s[i++] = tolower(fcc & 0xff);
    s[i] = 0;

    return s;
}

static int SetRegValue(int fccHandler, const char* name, int value)
{
    if (!name)
	return -1;
    char full_name[50];
    int result, status, newkey;
    result = RegCreateKeyExA(HKEY_CURRENT_USER,
			     GetKeyName(full_name, fccHandler),
			     0, 0, 0, 0, 0, &newkey, &status);
    if (result != 0)
	return -1;
    result = RegSetValueExA(newkey, name, 0, REG_DWORD, &value, 4);
    RegCloseKey(newkey);
    return result;
}

static int GetRegValue(int fccHandler, const char* name, int* place)
{
    if (!name || !place)
	return -1;

    char full_name[50];
    int result, status, newkey, count;
    result = RegOpenKeyExA(HKEY_CURRENT_USER,
			   GetKeyName(full_name, fccHandler),
			   0, 0, &newkey);
    if (result != 0)
	return -1;
    count=4;
    result = RegQueryValueExA(newkey, name, 0, 0, &status, &count);
    if (count != 4)
	result = -1;
    RegCloseKey(newkey);
    if (result == 0)
	*place = status;
    return result;
}

extern "C" int GetAttrInt(const CodecInfo& info, const char* attribute, int& value)
{
    if (attribute == 0)
	return -1;

    int result, newkey, status, count;

    switch (info.fourcc)
    {
    case fccDIV3:
    case fccDIV4:
    case fccDIV5:
    case fccDIV6:
	if ((strcmp(attribute, "Crispness") == 0)
	    || (strcmp(attribute, "KeyFrames") == 0))
	    return GetRegValue(info.fourcc, attribute, &value);
	if (strcmp(attribute, "BitRate") == 0)
	    return GetRegValue(info.fourcc, attribute, &value);
	if (strcmp(attribute, "Quality") == 0)
	{
	    const char* nkeyname;
	    if (info.kind==CodecInfo::Win32)
		nkeyname="SOFTWARE\\Microcrap\\Scrunch";
	    else
		nkeyname="SOFTWARE\\Microsoft\\Scrunch";
	    result = RegCreateKeyExA(HKEY_CURRENT_USER, nkeyname,
				     0, 0, 0, 0, 0, &newkey, &status);
	    if (result!=0)
	    {
		printf("VideoDecoder::GetExtAttr: registry failure\n");
		return -1;
	    }
	    count = 4;
	    result = RegQueryValueExA(newkey, "Current Post Process Mode",
				      0, &status, &value, &count);
	    if (result!=0)
	    {
		printf("VideoEncoder::GetExtAttr: no such value\n");
		value=-1;
		return -1;
	    }
	    RegCloseKey(newkey);
	    return result;
	}
	/* fall through */
    case fccWMV1:
    case fccWMV2:
	if ((strcmp(attribute, "Saturation") == 0)
	    || (strcmp(attribute, "Hue") == 0)
	    || (strcmp(attribute, "Contrast") == 0)
	    || (strcmp(attribute, "Brightness") == 0))
	{
	    result=RegCreateKeyExA(HKEY_CURRENT_USER, keyname, 0, 0, 0, 0, 0,	  		&newkey, &status);
	    if(result!=0)
	    {
		printf("VideoDecoder::GetExtAttr: registry failure\n");
		return -1;
	    }
	    count=4;
	    result=RegQueryValueExA(newkey, attribute, 0, &status, &value, &count);
	    if(result!=0)
	    {
		printf("VideoEncoder::GetExtAttr: no such value\n");
		value=50;
		return 0;
	    }
	    RegCloseKey(newkey);
	    return result;
	}
	break;
    case fccIV50:
	if (strcmp(attribute, "QuickCompress") == 0)
	{
	    result=RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\Intel\\Indeo\\5.0", 0, 0, 0, 0, 0,
				   &newkey, &status);
	    if(result!=0)
	    {
		printf("VideoEncoder::GetExtAttr: registry failure\n");
		return -1;
	    }
	    count=4;
	    result=RegQueryValueExA(newkey, "QuickCompress", 0, &status, &value, &count);
	    if(result!=0)
	    {
		printf("VideoEncoder::GetExtAttr: registry failure 2\n");
		return -1;
	    }
	    RegCloseKey(newkey);
	    return result;
	}
	printf("Unknown attribute '%s' for Intel Indeo 5.0 compressor\n", attribute);
	break;
    case fccIV41:

	if (strcmp(attribute, "QuickCompress") == 0)
	{
	    result = RegCreateKeyExA(HKEY_CURRENT_USER,
				     "Software\\Intel\\Indeo\\4.1",
				     0, 0, 0, 0, 0,
				     &newkey, &status);
	    if (result != 0)
	    {
		printf("VideoEncoder::GetExtAttr: registry failure\n");
		return -1;
	    }
	    count = 4;
	    result = RegQueryValueExA(newkey, "QuickCompress", 0,
				      &status, &value, &count);
	    if (result != 0)
	    {
		printf("VideoEncoder::GetExtAttr: registry failure 2\n");
		return -1;
	    }
	    RegCloseKey(newkey);
	    return result;
	}

	printf("Unknown attribute '%s' for Intel Indeo 4.1 compressor\n",
	       attribute);
	break;
    case fccMJPG:
	if (strcmp(attribute, "Mode") == 0)
	{
	    value = _GetPrivateProfileIntA("Compress", "Mode", 1, "M3JPEG.INI");
	    return 0;
	}
	printf("Unknown attribute '%s' for Motion JPEG compressor\n", attribute);
	break;
    }
    return -1;
}

extern "C" int SetAttrInt(const CodecInfo& info, const char* attribute, int value)
{
    int result, status, newkey, count;

    switch (info.fourcc)
    {
    case fccDIV3:
    case fccDIV4:
    case fccDIV5:
    case fccDIV6:
	if (strcmp(attribute, "Quality") == 0)
	{
	    if(info.kind==CodecInfo::Win32)
		keyname="SOFTWARE\\Microcrap\\Scrunch";
	    else
		keyname="SOFTWARE\\Microsoft\\Scrunch";
	    result=RegCreateKeyExA(HKEY_CURRENT_USER, keyname, 0, 0, 0, 0, 0,	   		&newkey, &status);
	    if(result!=0)
	    {
		printf("VideoDecoder::SetExtAttr: registry failure\n");
		return -1;
	    }
	    result=RegSetValueExA(newkey, "Current Post Process Mode", 0, REG_DWORD, &value, 4);
	    if(result!=0)
	    {
		printf("VideoDecoder::SetExtAttr: error writing value\n");
		return -1;
	    }
	    value=-1;
	    result=RegSetValueExA(newkey, "Force Post Process Mode", 0, REG_DWORD, &value, 4);
	    if (result != 0)
	    {
		printf("VideoDecoder::SetExtAttr: error writing value\n");
		return -1;
	    }
	    RegCloseKey(newkey);
	    return 0;
	}
	if ((strcmp(attribute, "Crispness") == 0)
	    || (strcmp(attribute, "KeyFrames") == 0))
	    return SetRegValue(info.fourcc, attribute, value);
	if (strcmp(attribute, "BitRate") == 0)
	    return SetRegValue(info.fourcc, attribute, value);
        /* fall through */
    case fccWMV1:
    case fccWMV2:
	if ((strcmp(attribute, "Saturation") == 0)
	    || (strcmp(attribute, "Hue") == 0)
	    || (strcmp(attribute, "Contrast") == 0)
	    || (strcmp(attribute, "Brightness") == 0))
	{
	    result=RegCreateKeyExA(HKEY_CURRENT_USER, keyname, 0, 0, 0, 0, 0,	   		&newkey, &status);
	    if(result!=0)
	    {
		printf("VideoDecoder::SetExtAttr: registry failure\n");
		return -1;
	    }
	    result=RegSetValueExA(newkey, attribute, 0, REG_DWORD, &value, 4);
	    if(result!=0)
	    {
		printf("VideoDecoder::SetExtAttr: error writing value\n");
		return -1;
	    }
	    RegCloseKey(newkey);
	    return 0;
	}
	printf("SetAttrInt: Unknown attribute '%s' for DivX/WMV compressor\n", attribute);
	return -1;
    case fccIV50:
	if (strcmp(attribute, "QuickCompress") == 0)
	{
	    result=RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\Intel\\Indeo\\5.0", 0, 0, 0, 0, 0,
				   &newkey, &status);
	    if(result!=0)
	    {
		printf("VideoEncoder::SetExtAttr: registry failure\n");
		return -1;
	    }
	    result=RegSetValueExA(newkey, "QuickCompress", 0, REG_DWORD, &value, 4);
	    if(result!=0)
	    {
		printf("VideoEncoder::SetExtAttr: registry failure 2\n");
		return -1;
	    }
	    RegCloseKey(newkey);
	    return result;
	}
	printf("SetAttrInt: Unknown attribute '%s' for Intel Indeo 5.0 compressor\n", attribute);
	return -1;
    case fccIV41:
	if (strcmp(attribute, "QuickCompress") == 0)
	{
	    result=RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\Intel\\Indeo\\4.1", 0, 0, 0, 0, 0,
				   &newkey, &status);
	    if(result!=0)return -1;
	    result=RegSetValueExA(newkey, "QuickCompress", 0, REG_DWORD, &value, 4);
	    RegCloseKey(newkey);
	    return result;
	}
	printf("SetAttrInt: Unknown attribute '%s' for Intel Indeo 4.1 compressor\n", attribute);
	return -1;
    case fccMJPG:
	if (strcmp(attribute, "Mode") == 0)
	{
	    char s[256];
	    sprintf(s, "%d", value);
	    _WritePrivateProfileStringA("Compress", "Mode", s, "M3JPEG.INI");
	    return 0;
	}
	printf("SetAttrInt: Unknown attribute '%s' for Motion JPEG compressor\n", attribute);
	return -1;
    }

    printf("SetAttrInt: Unknown attribute '%s'\n", attribute);
    return -1;
}

extern "C" int SetAttrString(const CodecInfo& info, const char* attribute, const char* value)
{
    if (!attribute)
	return -1;

    switch(info.fourcc)
    {
    case fccMJPG:
	if((!strcmp(attribute, "UserName")) || (!strcmp(attribute, "LicenseKey")))
	{
	    _WritePrivateProfileStringA("Register", attribute, value, "M3JPEG.INI");
	    return 0;
	}
	break;
    }

    return -1;
}

extern "C" int GetAttrString(const CodecInfo& info, const char* attribute, char* value, int size)
{
    if (!attribute)
	return -1;

    switch(info.fourcc)
    {
    case fccMJPG:
	if (strcmp(attribute, "UserName") == 0
	    || strcmp(attribute, "LicenseKey") == 0)
	    return _GetPrivateProfileStringA("Register", attribute, "", value, size, "M3JPEG.INI");
	break;
    }

    return -1;
}
