#include "aviplay_impl.h"
#include <configfile.h>
#include <cstring>
#include <cstdarg>
#include <cstdio>

using namespace std;

const string AviPlayer::regName = "AviPlayer";
const int AviPlayer::DEFAULT_0 = 1;
const int AviPlayer::DEFAULT_1 = 0;
const int AviPlayer::DEFAULT_RATE = 44100;
const int AviPlayer::DEFAULT_VOLUME = 1000;

// first in the list is default
const char* AviPlayer::videoRendererTxt =
{
#ifdef USE_SDL
    "SDL_XV,SDL_X11,"
#endif
    "novideo"
};

const char* AviPlayer::audioRendererTxt =
{
#ifdef USE_SDL
    "SDL,"
#endif
#ifdef OSS_SUPPORT
    "OSS,"
#endif
#ifdef SUNAUDIO_SUPPORT
    "SUN,"
#endif
    "noaudio"
};

const char* AviPlayer::subtitleRendererTxt =
{
#ifdef HAVE_XFT
    "XFT_DRAW,"
#endif
#ifndef WITHOUT_X
    "X11_DRAW,"
#endif
    "nosubtitles"
};

const AviPlayer::AviPlayerProperties AviPlayer::propertyList[] =
{
    // FIXME convert rest of flags later
    // text, type, defvalue, minval, maxval
    { "property_end" },
    { "AudioRenderer", RA_STRING, audioRendererTxt },
    { "VideoRenderer", RA_STRING, videoRendererTxt },
    { "SubtitleRenderer", RA_STRING, subtitleRendererTxt },
    { "VideoCodecs", RA_STRING, "" },
    { "AudioCodecs", RA_STRING, "" },
    { "UseYUV", RA_BOOL, &DEFAULT_1 },
    { "Autorepeat", RA_BOOL, &DEFAULT_1 },
    { "DefaultAudioStream", RA_INT, &DEFAULT_0, 0, 65536 },
    { "SubtitleFont", RA_STRING, "adobe-times" },
    { "SubtitleAsync_ms", RA_INT, &DEFAULT_0 },
    { "UseHttpProxy", RA_BOOL, &DEFAULT_0 },
    { "HttpProxy", RA_STRING, "" },
    { "PreserveAspect", RA_BOOL, &DEFAULT_1 },
    { "VideoBuffered", RA_BOOL, &DEFAULT_1 },
    { "VideoDirect", RA_BOOL, &DEFAULT_1 },
    { "VideoDropping", RA_BOOL, &DEFAULT_1 },
    { "QualityAuto", RA_BOOL, &DEFAULT_1 },
    { "DisplayFramePos", RA_BOOL, &DEFAULT_1 },
    { "AudioResampling", RA_BOOL, &DEFAULT_0 },
    { "AudioResamplingRate", RA_INT, &DEFAULT_RATE, 1, 100000 },
    { "AudioPlayingRate", RA_INT, &DEFAULT_RATE, 1, 100000 },
    { "AudioMasterTimer", RA_BOOL, &DEFAULT_0 },
    { "AudioVolume", RA_INT, &DEFAULT_VOLUME, 0, 10000 }, // 1000 is 1.0
    { "Async_ms", RA_INT, &DEFAULT_0 },
};

HRESULT AviPlayer::Set(...)
{
    va_list args;
    Property prop;

    va_start(args, this);
    while ((prop = (Property) va_arg(args, int)) != 0)
    {
	int int_param = 0;
	char* string_param = 0;

	if (prop < LAST_PROPERTY)
	{
	    //cout << "PROP " << propertyList[prop].text << endl;
	    switch (propertyList[prop].varType)
	    {
	    case RA_INT:
	    case RA_BOOL:
		int_param = va_arg(args, int);
		//cout << "INT " << int_param << endl;
		break;
	    case RA_STRING:
	    case RA_STRING_ARRAY:
		string_param = va_arg(args, char*);
                //cout << "STR " << string_param << endl;
		break;
	    }

	    switch (prop)
	    {
	    case AUDIO_RENDERER:
		break;
	    case VIDEO_RENDERER:
		break;
	    case USE_YUV:
		setUseYUV(int_param);
		break;
	    case VIDEO_QUALITY_AUTO:
		setVideoQualityAuto(int_param);
		break;
	    case VIDEO_BUFFERED:
		setVideoBuffered(int_param);
		break;
	    case VIDEO_DIRECT:
		setVideoDirect(int_param);
		break;
	    case VIDEO_DROPPING:
		m_bVideoDropping = int_param;
		break;
	    case ASYNC_TIME_MS:
		m_fAsync = int_param / 1000.0;
		if (m_pAudioRenderer)
		    m_pAudioRenderer->SetAsync(m_fAsync);
		break;
	    case SUBTITLE_FONT:
                setFont(string_param);
		break;
	    case SUBTITLE_ASYNC_TIME_MS:
                m_fSubAsync = int_param / 1000.0;
		break;
	    case AUDIO_VOLUME:
		m_fVolume = int_param / 1000.0;
		if (m_pAudioRenderer)
		    m_pAudioRenderer->SetVolume(m_fVolume);
		break;
	    default:
		//cout << "Unknown (?unsupported?) propety " << prop << endl;
		break;
	    }

	    switch (propertyList[prop].varType)
	    {
	    case RA_INT:
	    case RA_BOOL:
		Registry::WriteInt(regName, propertyList[prop].text, int_param);
		break;
	    case RA_STRING:
	    case RA_STRING_ARRAY:
		Registry::WriteString(regName, propertyList[prop].text, string_param);
		break;
	    }
	}
    }
    va_end(args);

    return 0;
}

HRESULT AviPlayer::Get(...) const
{
    va_list args;
    Property prop;

    //printf("Sizeof %d\n", sizeof(Property));
    va_start(args, this);
    while ((prop = (Property) va_arg(args, int)) != 0)
    {
	bool ok = true;
        bool isBool = false;
	char* string_param = 0; // if it stays 0 - we assume 'int'
	int int_param = 0;

	if (prop < LAST_PROPERTY)
	{
	    //printf("Property %d  %s  (readcnt: %d)\n", prop, propertyList[prop].text, propertyRead[prop]);
	    Property force = prop;

	    isBool = (propertyList[prop].varType == RA_BOOL);

	    if (!propertyRead[prop])
		force = LAST_PROPERTY; // force reading from registry

	    switch (force)
	    {
	    case VIDEO_BUFFERED:
                int_param = m_bVideoBuffered;
		break;
	    case VIDEO_DROPPING:
		int_param = m_bVideoDropping;
		break;
	    case VIDEO_DIRECT:
		int_param = m_bVideoDirect;
                break;
	    case VIDEO_QUALITY_AUTO:
		int_param = m_bQualityAuto;
		break;

	    case ASYNC_TIME_MS:
                int_param = int(m_fAsync * 1000.0);
		break;
	    case SUBTITLE_ASYNC_TIME_MS: // float
		int_param = int(m_fSubAsync * 1000.0);
		break;
	    case AUDIO_VOLUME: // float
		int_param = int(m_fVolume * 1000.0);
		break;

	    default:
		//printf("Default propety: %s (%d)\n", propertyList[prop].text, prop);
		switch (propertyList[prop].varType)
		{
		case RA_BOOL:
		case RA_INT:
		    int_param = Registry::ReadInt(regName,
						  propertyList[prop].text,
						  *(const int*)propertyList[prop].defValue);
		    break;
		case RA_STRING:
		case RA_STRING_ARRAY:
		    {
			const char* def = (propertyList[prop].varType == RA_STRING)
			    ? (const char*) propertyList[prop].defValue :
			    ((const char**) propertyList[prop].defValue)[0];

			//printf("DEFAULT: %s\n", def);
			string s = Registry::ReadString(regName,
							propertyList[prop].text,
							def);
			string_param = strdup(s.c_str());
		    }
                    break;
		}
		propertyRead[prop]++;
		break;
	    }
	}
	else
	{
	    switch (prop)
	    {
	    case QUERY_AVG_QUALITY:
		int_param = int(m_Quality.average());
		break;
	    case QUERY_AVG_DROP:
		int_param = int(m_Drop.average());
		break;
	    case QUERY_AUDIO_RENDERERS:
		string_param = (char*)audioRendererTxt;
                break;
	    case QUERY_VIDEO_RENDERERS:
		string_param = (char*)videoRendererTxt;
                break;
	    case QUERY_SUBTITLE_RENDERERS:
		string_param = (char*)subtitleRendererTxt;
                break;
	    default:
		cout << "Unexpected property value: " << prop << endl;
                abort();
		break;
	    }
	}

	int* ptr = va_arg(args, int*);
	//printf("Ptr:  %p\n", ptr);
	if (ok && ptr)
	{
	    if (isBool)
		*(bool*)ptr = (bool) int_param;
	    else
		*ptr = (string_param) ? (int)string_param : int_param;
	}
    }
    va_end(args);

    return 0;
}

int AviPlayer::setVideoQualityAuto(bool enabled)
{
    if (m_bQualityAuto != enabled)
    {
	m_bQualityAuto = enabled;
	if (m_pVideostream)
	{
	    IVideoDecoder* vd = m_pVideostream->GetDecoder();
	    if (vd)
	    {
		IVideoDecoder::DecodingMode mode = IVideoDecoder::DIRECT;
		if (m_bVideoBuffered)
		    if (m_bQualityAuto)
			mode = IVideoDecoder::REALTIME_QUALITY_AUTO;
		    else
			mode = IVideoDecoder::REALTIME;
		vd->SetDecodingMode(mode);
	    }
	}
    }

    return 0;
}

int AviPlayer::setVideoDirect(bool enabled)
{
    if (m_bVideoDirect != enabled)
    {
	bool pl = IsPlaying();
	if (pl)
	    lockThreads("SetVideoDirect");
	if (m_VideoRenderers.size() > 0)
	{
	    cout << "Checking direct... " << endl;
	    if (m_pVideostream && m_pVideostream->GetDecoder()
		&& m_pVideostream->GetDecoder()->SetDirectMemoryAccess((enabled) ? m_VideoRenderers[0] : 0) == 0)
	    {
		cout << "Direct mode is ON" << endl;
		m_bVideoDirect = true;
	    }
	    else
	    {
		cout << "Direct mode is OFF" << endl;
		m_bVideoDirect = false;
	    }
	}
	if (pl)
	    unlockThreads();
    }

    return 0;
}

int AviPlayer::setVideoBuffered(bool enabled)
{
    if (m_bVideoBuffered != enabled)
    {
	m_bVideoBuffered = enabled;
	bool pl = IsPlaying();
	if (pl)
	    lockThreads("SetVideoBuffered");

	IVideoDecoder::DecodingMode mode = IVideoDecoder::DIRECT;

	if (m_bVideoBuffered)
	    if (m_bQualityAuto)
		mode = IVideoDecoder::REALTIME_QUALITY_AUTO;
	    else
		mode = IVideoDecoder::REALTIME;

	if (m_pVideostream && m_pVideostream->GetDecoder())
            m_pVideostream->GetDecoder()->SetDecodingMode(mode);// == 0)

	if (pl)
	    unlockThreads();
    }
    return 0;
}
