#include "FFAudioDecoder.h"
#include "FFVideoDecoder.h"
#include "fillplugins.h"

#include <plugin.h>
#include <except.h>
#include <fourcc.h>

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

using namespace std;

static vector<CodecInfo> libffmpeg_codecs;
static string last_error;
static bool initialized = false;

// figure out which codec we need to use
static AVCodec* avcodec_find_decoder_by_fcc(fourcc_t fcc)
{
    // translation table
    static const struct fcc_to_avcodecid {
	enum CodecID codec;
	fourcc_t list[4]; // maybe we could map more fcc to same codec
    } lc[] = {
	{ CODEC_ID_MSMPEG4, { fccDIV3, 0 } },
	{ CODEC_ID_OPENDIVX, { fccDIVX, 0 } },
	{ CODEC_ID_MJPEG, { fccMJPG, 0 } },
	{ CODEC_ID_AC3, { 0x2000, 0 } },
	//{ CODEC_ID_H263, { fccH263, 0 } },
	{ CODEC_ID_NONE, {0}}
    };

    for (const fcc_to_avcodecid* c = lc; c->codec != CODEC_ID_NONE; c++)
    {
	int i = 0;
	while (c->list[i] != 0)
	    if (c->list[i++] == fcc)
		return avcodec_find_decoder(c->codec);
    }

    return NULL;
}

static void plugin_init()
{
    avcodec_init();
    avcodec_register_all();
    initialized = true;
}

extern "C" const vector<CodecInfo>& RegisterPlugin()
{
    if (libffmpeg_codecs.size())
	return libffmpeg_codecs;

    libffmpeg_FillPlugins(libffmpeg_codecs);
    return libffmpeg_codecs;
}

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

extern "C" IVideoEncoder* CreateVideoEncoder(const CodecInfo& info, fourcc_t compressor, const BITMAPINFOHEADER& bh)
{
    try
    {
	if (!initialized)
	    plugin_init();

	return 0;//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 (!initialized)
	    plugin_init();

	AVCodec* av = avcodec_find_decoder_by_fcc(info.fourcc);
	return (av) ? new FFVideoDecoder(av, info, bh, flip) : 0;
    }
    catch (FatalError& e)
    {
	last_error = e.GetDesc();
	e.Print();
	return 0;
    }
}

extern "C" IAudioDecoder* CreateAudioDecoder(const CodecInfo& info, const WAVEFORMATEX* fmt)
{
    try
    {
	if (!initialized)
	    plugin_init();

	AVCodec* av = avcodec_find_decoder_by_fcc(info.fourcc);
	return (av) ? new FFAudioDecoder(av, info, fmt) : 0;

	return 0;
    }
    catch (FatalError& e)
    {
	last_error = e.GetDesc();
	e.Print();
	return 0;
    }
}

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

int SetRegValue(fourcc_t fccHandler, const char* name, int value)
{
    return 0;
}

int GetRegValue(fourcc_t fccHandler, const char* name, int* place)
{
    return 0;
}

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, "Saturation") == 0)
	    || (strcmp(attribute, "Hue") == 0)
	    || (strcmp(attribute, "Contrast") == 0)
	    || (strcmp(attribute, "Brightness") == 0))
	{
	    return 0;
	}
	break;
    case fccMJPG:
	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, "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 */
	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:
	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:
	break;
    }

    return -1;
}
