/*
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#ifndef ANDROID_AUDIO_MIO_H
#define ANDROID_AUDIO_MIO_H

#ifndef PVMI_MIO_CONTROL_H_INCLUDED
#include "pvmi_mio_control.h"
#endif
#ifndef PVMI_MEDIA_TRANSFER_H_INCLUDED
#include "pvmi_media_transfer.h"
#endif
#ifndef OSCL_SCHEDULER_AO_H_INCLUDED
#include "oscl_scheduler_ao.h"
#endif
#ifndef PVMI_MEDIA_IO_OBSERVER_H_INCLUDED
#include "pvmi_media_io_observer.h"
#endif
#ifndef OSCL_FILE_IO_H_INCLUDED
#include "oscl_file_io.h"
#endif
#ifndef PVMI_CONFIG_AND_CAPABILITY_H_INCLUDED
#include "pvmi_config_and_capability.h"
#endif
#ifndef OSCL_STRING_CONTAINERS_H_INCLUDED
#include "oscl_string_containers.h"
#endif
#ifndef PVMI_MEDIA_IO_CLOCK_EXTENSION_H_INCLUDED
#include "pvmi_media_io_clock_extension.h"
#endif

#ifndef ANDROID_AUDIO_OUTPUT_THREADSAFE_CALLBACK_AO_H_INCLUDED
#include "android_audio_output_threadsafe_callbacks.h"
#endif
#ifndef PVMF_MEDIA_CLOCK_H_INCLUDED
#include "pvmf_media_clock.h"
#endif
#ifdef PERFORMANCE_MEASUREMENTS_ENABLED
#ifndef _PVPROFILE_H
#include "pvprofile.h"
#endif
#endif


#include <media/MediaPlayerInterface.h>

class PVLogger;
class PVMFMediaClock;


using namespace android;

// Write command handling
// the audio output thread queues the responses, this mio dequeues and processes them
class WriteResponse
{
    public:
        WriteResponse(PVMFStatus s, PVMFCommandId id, const OsclAny* ctx, const PVMFTimestamp& ts) :
                iStatus(s), iCmdId(id), iContext(ctx), iTimestamp(ts)
        {}

        PVMFStatus iStatus;
        PVMFCommandId iCmdId;
        const OsclAny* iContext;
        PVMFTimestamp iTimestamp;
};

// Active timing support
class AndroidAudioMIOActiveTimingSupport :
        public PvmiClockExtensionInterface, public PVMFMediaClockStateObserver
{
    public:

        AndroidAudioMIOActiveTimingSupport(int32 minCorrection = 0, int32 maxCorrection = 0) :
                iClock(NULL),
                iClockNotificationsInf(NULL),
                iClockState(PVMFMediaClock::STOPPED),
                iAudioThreadSem(0),
                iStartTime(0),
                iFrameCount(0),
                iMsecsPerFrame(25),
                iDriverLatency(0),
                iUpdateClock(true),
                iMinCorrection(minCorrection),
                iMaxCorrection(maxCorrection)
        {
        }

        ~AndroidAudioMIOActiveTimingSupport()
        {
            if (iClock && iClockNotificationsInf)
            {
                iClockNotificationsInf->RemoveClockStateObserver(*this);
                iClock->DestroyMediaClockNotificationsInterface(iClockNotificationsInf);
                iClockNotificationsInf = NULL;
            }
        }

        //from PvmiClockExtensionInterface
        OSCL_IMPORT_REF PVMFStatus SetClock(PVMFMediaClock* clockVal);

        // from PVMFMediaClockStateObserver
        void ClockStateUpdated();

        //from PVInterface
        OSCL_IMPORT_REF void addRef() {}
        OSCL_IMPORT_REF void removeRef() {}
        OSCL_IMPORT_REF bool queryInterface(const PVUuid& uuid, PVInterface*& iface);
        void NotificationsInterfaceDestroyed();

        void ForceClockUpdate()
        {
            iUpdateClock = true;
        }
        void UpdateClock();

        PVMFMediaClock::PVMFMediaClockState clockState()
        {
            return iClockState;
        }
        void setThreadSemaphore(OsclSemaphore* s)
        {
            iAudioThreadSem = s;
        }

        void setDriverLatency(uint32 latency);
        void incFrameCount(uint32_t numFrames)
        {
            iFrameCount += numFrames;
        }
        void setFrameRate(float msecsPerFrame)
        {
            iMsecsPerFrame = msecsPerFrame;
        }
        float msecsPerFrame()
        {
            return iMsecsPerFrame;
        }

    private:
        PVMFMediaClock* iClock;
        PVMFMediaClockNotificationsInterface *iClockNotificationsInf;
        PVMFMediaClock::PVMFMediaClockState iClockState;
        OsclSemaphore* iAudioThreadSem;

        uint32 iStartTime;
        uint32 iFrameCount;
        float iMsecsPerFrame;
        uint32 iDriverLatency;
        bool iUpdateClock;
        int32 iMinCorrection;
        int32 iMaxCorrection;
};

// Audio MIO component
class AndroidAudioMIO : public OsclTimerObject,
        public PvmiMIOControl,
        public PvmiMediaTransfer,
        public PvmiCapabilityAndConfig
{
    public:
        OSCL_IMPORT_REF AndroidAudioMIO(const char* name);

        OSCL_IMPORT_REF ~AndroidAudioMIO();

        // APIs from PvmiMIOControl

        PVMFStatus connect(PvmiMIOSession& aSession, PvmiMIOObserver* aObserver);

        PVMFStatus disconnect(PvmiMIOSession aSession);

        virtual PVMFCommandId QueryInterface(const PVUuid& aUuid, PVInterface*& aInterfacePtr, const OsclAny* aContext = NULL);

        PvmiMediaTransfer* createMediaTransfer(PvmiMIOSession& aSession, PvmiKvp* read_formats = NULL, int32 read_flags = 0,
                                               PvmiKvp* write_formats = NULL, int32 write_flags = 0);

        void deleteMediaTransfer(PvmiMIOSession& aSession, PvmiMediaTransfer* media_transfer) {}

        PVMFCommandId Init(const OsclAny* aContext = NULL);

        PVMFCommandId Reset(const OsclAny* aContext = NULL);

        PVMFCommandId Start(const OsclAny* aContext = NULL);

        PVMFCommandId Pause(const OsclAny* aContext = NULL);

        PVMFCommandId Flush(const OsclAny* aContext = NULL);

        virtual PVMFCommandId DiscardData(const OsclAny* aContext = NULL);

        virtual PVMFCommandId DiscardData(PVMFTimestamp aTimestamp = 0, const OsclAny* aContext = NULL);

        virtual PVMFCommandId Stop(const OsclAny* aContext = NULL);

        virtual PVMFCommandId CancelAllCommands(const OsclAny* aContext = NULL);

        virtual PVMFCommandId CancelCommand(PVMFCommandId aCmdId, const OsclAny* aContext = NULL);

        virtual void cancelCommand(PVMFCommandId aCmdId) {}

        void ThreadLogon();

        void ThreadLogoff();

        // APIs from PvmiMediaTransfer

        void setPeer(PvmiMediaTransfer* aPeer);

        void useMemoryAllocators(OsclMemAllocator* write_alloc = NULL) {}

        PVMFCommandId writeAsync(uint8 format_type, int32 format_index,
                                 uint8* data, uint32 data_len,
                                 const PvmiMediaXferHeader& data_header_info,
                                 OsclAny* aContext = NULL);

        void writeComplete(PVMFStatus aStatus, PVMFCommandId  write_cmd_id, OsclAny* aContext) {}

        PVMFCommandId readAsync(uint8* data, uint32 max_data_len, OsclAny* aContext = NULL,
                                int32* formats = NULL, uint16 num_formats = 0)
        {
            OsclError::Leave(OsclErrNotSupported);
            return -1;
        }

        void readComplete(PVMFStatus aStatus, PVMFCommandId  read_cmd_id, int32 format_index,
                          const PvmiMediaXferHeader& data_header_info, OsclAny* aContext) {}

        void statusUpdate(uint32 status_flags) {}

        virtual void cancelAllCommands()
        {
            returnAllBuffers();
        }

        // Pure virtuals from PvmiCapabilityAndConfig

        void setObserver(PvmiConfigAndCapabilityCmdObserver* aObserver)
        {
            OSCL_UNUSED_ARG(aObserver);
        }

        PVMFStatus getParametersSync(PvmiMIOSession aSession, PvmiKeyType aIdentifier,
                                     PvmiKvp*& aParameters, int& num_parameter_elements, PvmiCapabilityContext aContext);

        PVMFStatus releaseParameters(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements);

        void createContext(PvmiMIOSession aSession, PvmiCapabilityContext& aContext)
        {
            OsclError::Leave(OsclErrNotSupported);
        }

        void setContextParameters(PvmiMIOSession aSession, PvmiCapabilityContext& aContext,
                                  PvmiKvp* aParameters, int num_parameter_elements)
        {
            OsclError::Leave(OsclErrNotSupported);
        }

        void DeleteContext(PvmiMIOSession aSession, PvmiCapabilityContext& aContext)
        {
            OsclError::Leave(OsclErrNotSupported);
        }

        virtual void setParametersSync(PvmiMIOSession aSession, PvmiKvp* aParameters,
                                       int num_elements, PvmiKvp * & aRet_kvp);

        PVMFCommandId setParametersAsync(PvmiMIOSession aSession, PvmiKvp* aParameters,
                                         int num_elements, PvmiKvp*& aRet_kvp, OsclAny* context = NULL)
        {
            OsclError::Leave(OsclErrNotSupported);
            return -1;
        }

        uint32 getCapabilityMetric(PvmiMIOSession aSession)
        {
            return 0;
        }

        PVMFStatus verifyParametersSync(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements);

        // Functions specific to this MIO
        void setAudioSink(const sp<MediaPlayerInterface::AudioSink>& audioSink);

        virtual void writeAudioBuffer(uint8* aData, uint32 aDataLen, PVMFCommandId cmdId,
                                      OsclAny* aContext, PVMFTimestamp aTimestamp) {}

    protected:
        // From OsclTimerObject
        void Run();
        void sendResponse(PVMFCommandId cmdid, const OsclAny* context, PVMFTimestamp timestamp);

        // Specific to this MIO
        void initData();

        void Cleanup();

        virtual void returnAllBuffers() {}

        void ResetData();

        // request active object which the audio output thread uses to schedule this timer object to run
        AndroidAudioOutputThreadSafeCallbackAO *iWriteCompleteAO;

        // write response queue, needs to use lock mechanism to access
        Oscl_Vector<WriteResponse, OsclMemAllocator> iWriteResponseQueue;

        // lock used to access the write response queue
        OsclMutex iWriteResponseQueueLock;

        // From setPeer
        PvmiMediaTransfer* iPeer;

        // The PvmiMIOControl class observer.
        PvmiMIOObserver* iObserver;

        // for generating command IDs
        uint32 iCommandCounter;

        //State
        enum PVAudioMIOState
        {
            STATE_MIO_IDLE
            , STATE_MIO_LOGGED_ON
            , STATE_MIO_INITIALIZED
            , STATE_MIO_STARTED
            , STATE_MIO_PAUSED
        };
        PVAudioMIOState iState;

        // Control command handling.
        class CommandResponse
        {
            public:
                CommandResponse(PVMFStatus s, PVMFCommandId id, const OsclAny* ctx)
                        : iStatus(s), iCmdId(id), iContext(ctx)
                {}

                PVMFStatus iStatus;
                PVMFCommandId iCmdId;
                const OsclAny* iContext;
        };
        Oscl_Vector<CommandResponse, OsclMemAllocator> iCommandResponseQueue;
        PVMFCommandId QueueCmdResponse(PVMFStatus status, const OsclAny* aContext);
        void ProcessWriteResponseQueue();

        // Audio parameters.
        PVMFFormatType iAudioFormat;
        int32 iAudioNumChannels;
        bool iAudioNumChannelsValid;
        int32 iAudioSamplingRate;
        bool iAudioSamplingRateValid;

        int32 iNumberOfBuffers;
        int32 iBufferSize;
        bool iNumberOfBuffersValid;
        bool iBufferSizeValid;


        bool iAudioThreadCreatedAndMIOConfigured;

        // For logging
        PVLogger* iLogger;

        // For implementing the write flow control
        bool iWriteBusy;
        bool CheckWriteBusy(uint32);

        bool iFlushPending;
        uint32 iDataQueued;

        sp<MediaPlayerInterface::AudioSink>   mAudioSink;
};

#endif // ANDROID_AUDIO_MIO_H

