#ifndef AVIPLAY_IMPL_H
#define AVIPLAY_IMPL_H

#include <aviplay.h>
#include <avifile.h>
#include <videodecoder.h>
#include <videoencoder.h>
#include <audiodecoder.h>
#include <renderer.h>
#include <except.h>
#include "Statistic.h"
#include "IAudioRenderer.h"
#include <Locker.h>

#include <unistd.h> // geteuid

#include <vector>

/***************************************************

Here aviplay interface goes

****************************************************/


class AviPlayer: public IAviPlayer2
{
public:
    AviPlayer(const char* filename, int _depth, const char*subname) throw(FatalError);
    ~AviPlayer();
    virtual bool IsOpened() const
    {
	return (m_pClip) ? m_pClip->IsOpened() : true;
    }

    virtual bool IsValid() const
    {
	return (m_pClip) ? m_pClip->IsValid() : false;
    }

    virtual bool IsRedirector() const
    {
	return (m_pClip) ? m_pClip->IsRedirector() : false;
    }

    virtual bool GetURLs(std::vector<std::string>& urls) const
    {
	return (m_pClip) ? m_pClip->GetURLs(urls) : false;
    }

    HRESULT SetColorSpace(fourcc_t csp, bool test_only);

    int GetWidth() const {return m_iWidth;}
    int GetHeight() const {return labs(m_iHeight);}
    void SetKillHandler(KILLHANDLER handler, void* arg=0)
    {
	m_pKillhandler=handler;
	m_pKillhandlerArg=arg;
    }
    void SetAudioFunc(AUDIOFUNC func, void* arg=0)
    {
	m_pAudiofunc=func;
	m_pAudiofuncArg=arg;
    }
    double GetPos() const;
    framepos_t GetFramePos() const;
    double Reseek(double pos);
    HRESULT ReseekExact(double pos);
    HRESULT PageUp();
    HRESULT PageDown();
    HRESULT NextFrame();
    void Play();
    void Start();
    void Stop();
    void Pause(bool state);
    const subtitle_line_t* GetCurrentSubtitles();
    virtual double GetLengthTime() const
    {
	double len = GetVideoLengthTime();
        double alen = GetAudioLengthTime();
	if (len < alen)
	    len = alen;
        return len;
    }
    virtual double GetAudioLengthTime() const
    {
	return (m_pAudioRenderer) ? m_pAudioRenderer->GetLengthTime() : 0;
    }
    virtual double GetVideoLengthTime() const
    {
	return (m_pVideostream) ? m_pVideostream->GetLengthTime() : 0;
    }

    virtual bool IsStopped() const;
    virtual bool IsPlaying() const;
    virtual bool IsPaused() const;

    virtual const char* GetAudioFormat() const
    {
	return (m_pAudioRenderer) ? m_pAudioRenderer->GetAudioFormat() : 0;
    }
    virtual StreamInfo* GetAudioStreamInfo() const;

    virtual const char* GetVideoFormat() const
    {
	if (!m_pVideostream)
	    return "";
	BITMAPINFOHEADER bh;
	m_pVideostream->GetVideoFormatInfo(&bh, sizeof(bh));
	const CodecInfo* ci=CodecInfo::match(bh.biCompression, CodecInfo::Video);

	return (ci) ?  ci->text.c_str() : "unknown";
    }
    virtual StreamInfo* GetVideoStreamInfo() const;
    virtual double GetFps() const
    {
	return (m_pVideostream) ? 1.0 / m_pVideostream->GetFrameTime() : 0.0;
    }
    virtual const char* GetFileName() const
    {
	return m_Fn.c_str();
    }
    virtual const std::vector<VideoRenderer*>& GetVideoRenderers() const;
    virtual HRESULT SetVideoRenderers(std::vector<VideoRenderer*>);
    virtual State GetState(double* percent);
    virtual bool HasSubtitles() const;
    virtual HRESULT InitSubtitles(const char* filename);
    virtual HRESULT Set(...);
    virtual HRESULT Get(...) const;
    virtual void SetAsync(float async);
    virtual float GetAsync() const { return m_fAsync; }
    virtual const CodecInfo& GetCodecInfo() const
    {
	return m_pVideostream->GetDecoder()->GetCodecInfo();
    }
    virtual IRtConfig* GetRuntimeConfig() const {return dynamic_cast<IRtConfig*>(m_pVideostream->GetDecoder());}
    virtual void Restart();

    virtual void SetVideo(bool enabled) { m_bVideoMute = enabled; }
    virtual void SetAudio(bool enabled) { m_bAudioMute = enabled; }
    virtual bool GetVideo() const { return m_bVideoMute; }
    virtual bool GetAudio() const { return m_bAudioMute; }

    virtual int Refresh() { return -1; }
    virtual int Resize(int& new_w, int& new_h) { return -1; }
    virtual int ToggleFullscreen(bool maximize = false) { return -1; }

protected:
    enum var_t
    {
	RA_BOOL, RA_INT, RA_STRING, RA_STRING_ARRAY
    };

    struct AviPlayerProperties
    {
	const char* text;
	var_t varType;
	const void* defValue;
	int min;
        int max;
    };
    static const int DEFAULT_0;
    static const int DEFAULT_1;
    static const int DEFAULT_RATE;
    static const int DEFAULT_VOLUME;
    static const std::string regName;
    static const char* videoRendererTxt;
    static const char* audioRendererTxt;
    static const char* subtitleRendererTxt;
    static const AviPlayerProperties propertyList[];

    mutable int AviPlayer::propertyRead[LAST_PROPERTY]; // have we read at least once from Registry::

    std::vector<VideoRenderer*> m_VideoRenderers; // we could draw image to more places
    IAudioRenderer* m_pAudioRenderer; // not sure about the sound - this will be
                                      // more complicated because of buffer sizes

    KILLHANDLER m_pKillhandler;
    void* m_pKillhandlerArg;
    AUDIOFUNC m_pAudiofunc;
    void* m_pAudiofuncArg;
    IAviReadFile* m_pClip;
    IAviReadStream* m_pVideostream;
    IAviReadStream* m_pAudiostream;
    Statistic m_Drop;
    Statistic m_Quality;
    std::string m_Fn;
    std::string m_Sn;
    subtitles_t* m_pSubtitles;	// list of subtitles
    subtitle_line_t* m_pSubline;// contains current subtitle line

    bool m_bVideoMute;
    bool m_bAudioMute;
    bool m_bVideoAsync;		// true - do not care about a-v sync
    bool m_bQualityAuto;
    bool m_bVideoBuffered;
    bool m_bVideoDirect;
    bool m_bVideoDropping;

    bool m_bPaused;		// true if we are paused
    bool m_bInitialized;	// true if we are playing OR paused
    bool m_bQuit;		// true signals to all processing threads
				// to terminate

    bool m_bHangup;		// signals main_thread to enter 'waiting' state
				// and set initialized=0
    bool m_bBuffering;

    bool m_bCallSync;           // marks that video update is needed



    float m_fVolume;		// current volume level
    float m_fAsync;		// a-v async time
    float m_fSubAsync;
    int m_iFramesVideo;		// just counters
    int m_iFrameDrop;
    int m_iLockCount;

    int m_iWidth;
    int m_iHeight;
				// these vars are used in syncing video with time
    int64_t m_lTimeStart;	// timestamp of moment when we started playing
				// updated after each reseek or pause
    double m_dFrameStart;	// precise position of video at time_start ( needed
				// for timing of video-only streams )

    int64_t m_lLastVideoSync;	// stamp for last video time in sync
    double m_dLastAudioSync;	// stamp for last audio time in sync

    int64_t m_lLastTimeStart;	// timestamp of the last displayed frame
    double m_dLastFrameStart;	// precise position of video at last frame
    float m_fLastDiff;          // difference between succesive
    pthread_t m_VideoThread;	// performs video output and sync
    pthread_t m_AudioThread;	// performs audio output and sync
    pthread_t m_DecoderThread;	// performs video decompression and caching

    uid_t m_iEffectiveUid;	// effective user id
    gid_t m_iEffectiveGid;	// effective group id

    enum ThreadId
    {   // do not changed order
	THREAD_FIRST = 0,
	THREAD_DECODER = THREAD_FIRST, THREAD_AUDIO, THREAD_VIDEO,
	THREAD_LAST,
    };

    PthreadMutex m_ThreadMut[THREAD_LAST]; // 3 mutexes
    PthreadCond m_ThreadCond[THREAD_LAST]; // 3 conditions
    PthreadMutex m_DropMutex;
    PthreadMutex m_LockMutex;

    double m_dVideoSeekDest;
    double m_dVframetime;
    int m_iDepth;
    static void* constructThread(void* arg);
    void* constructThreadfunc();
    void construct();
    void lockThreads(const char *name = NULL);
    void unlockThreads(void);
    int checkSync(ThreadId);
    int drawFrame();
    void syncFrame();
    void changePriority(const char* taskName, int add, int schedt = 0);

    void* videoThread();
    void* audioThread();
    void* decoderThread();

    // special way to have function  compatible with C code pthread parameter
    // we could use only one parameter and this seems to be simpliest way
    static void* startVideoThread(void* arg);
    static void* startAudioThread(void* arg);
    static void* startDecoderThread(void* arg);

    double getVideoAsync();
    bool dropFrame(bool leaveLocked = false);

    virtual int setVideoQualityAuto(bool enabled);
    virtual int setVideoBuffered(bool enabled);
    virtual int setVideoDirect(bool enabled);
    virtual int setUseYUV(bool enabled) { return -1; }
    virtual int setFont(const char* fn) { return -1; }
};

#endif // AVIPLAY_IMPL_H
