/*
#  $Id$
# -----------------------------------------------------------------------------
#  The part of 'VideoNEXT MediaClient SDK'
# -----------------------------------------------------------------------------
#  Author: Petrov Maxim, 06/19/2012
#  Edited by:
#  QA by:
#  Copyright: videoNEXT LLC
# -----------------------------------------------------------------------------
*/
#ifndef _PLAYOUTBUFFER_H_
#define _PLAYOUTBUFFER_H_

#include "ace/Reactor.h"
#include "ace/TP_Reactor.h"
#include "ace/Thread_Manager.h"
#include "ace/Synch_Traits.h"
#include "ace/Recursive_Thread_Mutex.h"

#include <list>

#include "c_ptr.h"
//#include "Timeval.h"
#include "AdaptiveUsageEnvironment.h"

namespace videonext { namespace media {

class AudioVideoPlaybackController;

class PlayoutList
{
public:
    PlayoutList(AdaptiveUsageEnvironment& env);
    virtual ~PlayoutList();

    /**
     * Insert frame in to list
     *
     *
     */
    void insert(const CPMediaFrame &);


    /**
     * Lentgth of list (time beween last and first frame)
     *
     *
     * @return
     */
    ACE_Time_Value length(MediaFrame::MEDIA_TYPE media_type = MediaFrame::VIDEO) const;


    /**
     * Size of list (frames number)
     *
     *
     * @return
     */
    size_t size(MediaFrame::MEDIA_TYPE media_type = MediaFrame::VIDEO) const;

    /**
     * Get next frame from list. Note: list should NOT be empty
     *
     * @param [out] delayToNextFrame
     *
     * @return
     */
//    CPMediaFrame getNextFrame(ACE_Time_Value *delayToNextFrame = 0);

    CPMediaFrame getNextFrame(int type = -1, ACE_Time_Value *delayToNextFrame = 0);

    /**
     * Clear the list
     *
     */
    void clear();

    void print();

private:
    AdaptiveUsageEnvironment& fEnv;
    std::list<CPMediaFrame>  fFrameList[2]; // for audio and video
    ACE_Recursive_Thread_Mutex lock_;
};

class PlayoutBuffer : public ACE_Event_Handler
{
public:
    static PlayoutBuffer *open(AdaptiveUsageEnvironment& env,
                               AudioVideoPlaybackController *playbackController,
                               unsigned bufferLen)
    {
        return new PlayoutBuffer(env, playbackController, bufferLen);
    }

    static void close(PlayoutBuffer *buffer)
    {
        buffer->close();
    }

private:
   /**
     * ctor
     *
     * @param env
     * @param playbackController
     * @param bufferLen Buffer length in milliseconds
     */
    PlayoutBuffer(AdaptiveUsageEnvironment& env,
                  AudioVideoPlaybackController *playbackController,
                  unsigned bufferLen);

    virtual ~PlayoutBuffer();

    void close();

public:
    /**
     * Schedule frame for playback
     *
     * @param cpMediaFrame
     */
    void scheduleFrame(CPMediaFrame &cpMediaFrame);

    void setPaused(bool value);

    void flush();

    void soft_flush();

protected:

    void playFrame(int frame_type);

public:
    virtual int handle_timeout(const ACE_Time_Value &current_time, const void *act);

private:
    AdaptiveUsageEnvironment &fEnv;
    AudioVideoPlaybackController *fPlaybackController;
    ACE_Time_Value fBufferLen;

private:
    PlayoutList fPlayoutList;
    ACE_Time_Value fNextPlayTime[2];
    ACE_Time_Value fPrevPlayTime[2];
    ACE_Time_Value fSumDuration[2];
    ACE_Time_Value fSumRealDuration[2];
    
    ACE_Recursive_Thread_Mutex fTimerLock;

    ACE_Reactor *fReactor;
    int fReactorThreadId;
    bool fPaused;
    bool fBuffering;
};

}}

#endif /* _PLAYOUTBUFFER_H_ */
