/*
 * Copyright (C) 2010 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.
 */
#include <binder/ProcessState.h>
#include <sys/stat.h>
#include <media/AudioTrack.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AHandler.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>

#include "NuCachedSource2.h"
#include "NuHTTPDataSource.h"
#include "ThrottledSource.h"

#ifdef USERDEBUG_BUILD
#define _DEBUG_AUDIO_TESTS 1
#endif

#define DURATION_CACHED_HIGH_US  30000000 // 30s
#define DURATION_CACHED_MED_US   10000000 // 10s
#define DURATION_CACHED_LOW_US    2000000 //  2s

/*
 * by how much time data is written to the AudioTrack ahead of the scheduled render time
 */
#define RENDER_SAFETY_DELAY_US 5000 // 5ms

#define SIZE_CACHED_HIGH_BYTES 1000000
#define SIZE_CACHED_MED_BYTES   700000
#define SIZE_CACHED_LOW_BYTES   400000

/*
 * events sent to mNotifyClient during prepare, prefetch, and playback
 */
#define EVENT_PREPARED                "prep"
#define EVENT_PREFETCHSTATUSCHANGE    "prsc"
#define EVENT_PREFETCHFILLLEVELUPDATE "pflu"
#define EVENT_ENDOFSTREAM             "eos"
#define EVENT_NEW_AUDIOTRACK          "nwat"

#define SFPLAYER_SUCCESS 1
#define SFPLAYER_FD_FIND_FILE_SIZE ((int64_t)0xFFFFFFFFFFFFFFFFll)

#define NO_FILL_LEVEL_UPDATE -1
#define NO_STATUS_UPDATE kStatusUnknown


typedef struct AudioPlayback_Parameters_struct {
    int streamType;
    int sessionId;
    android::AudioTrack::callback_t trackcb;
    void* trackcbUser;
} AudioPlayback_Parameters;


namespace android {

    typedef void (*notif_client_t)(int event, const int data1, void* notifUser);

struct SfPlayer : public AHandler {
    SfPlayer(AudioPlayback_Parameters *app);

    enum CacheStatus {
        kStatusUnknown = -1,
        kStatusEmpty = 0,
        kStatusLow,
        kStatusIntermediate,
        kStatusEnough,
        kStatusHigh
    };

    enum {
        kEventPrepared                = 'prep',
        kEventPrefetchStatusChange    = 'prsc',
        kEventPrefetchFillLevelUpdate = 'pflu',
        kEventEndOfStream             = 'eos',
        kEventNewAudioTrack           = 'nwat'
    };

    void armLooper();
    void setNotifListener(const notif_client_t cbf, void* notifUser);

    void setDataSource(const char *uri);
    void setDataSource(const int fd, const int64_t offset, const int64_t length);

    void setCacheFillUpdateThreshold(int16_t thr) { mCacheFillNotifThreshold = thr; }

    void prepare();
    void play();
    void pause();
    void stop();
    void seek(int64_t timeMsec);
    void loop(bool loop);
    bool wantPrefetch();
    void startPrefetch_async();

    /**
     * returns the duration in microseconds, -1 if unknown
     */
    int64_t getDurationUsec() { return mDurationUsec; }
    int32_t getNumChannels()  { return mNumChannels; }
    int32_t getSampleRateHz() { return mSampleRateHz; }
    AudioTrack* getAudioTrack() { return mAudioTrack; }
    uint32_t getPositionMsec();

protected:
    virtual ~SfPlayer();
    virtual void onMessageReceived(const sp<AMessage> &msg);

private:

    enum {
        kDataLocatorNone = 'none',
        kDataLocatorUri  = 'uri',
        kDataLocatorFd   = 'fd',
    };

    enum {
        kWhatPrepare    = 'prep',
        kWhatDecode     = 'deco',
        kWhatRender     = 'rend',
        kWhatCheckCache = 'cach',
        kWhatNotif      = 'noti',
        kWhatPlay       = 'play',
        kWhatPause      = 'paus',
        kWhatSeek       = 'seek',
        kWhatLoop       = 'loop',
    };

    enum {
        kFlagPlaying   = 1,
        kFlagPreparing = 2,
        kFlagBuffering = 4,
        kFlagSeeking   = 8,
        kFlagLooping   = 16,
    };

    struct FdInfo {
        int fd;
        int64_t offset;
        int64_t length;
    };

    union DataLocator {
        char* uri;
        FdInfo fdi;
    };
#ifdef _DEBUG_AUDIO_TESTS
    FILE *mMonitorAudioFp; // Automated tests
#endif
    // mutex used for seek flag and seek time read/write
    Mutex       mSeekLock;

    AudioTrack *mAudioTrack;

    sp<ALooper> mRenderLooper;
    sp<DataSource> mDataSource;
    sp<MediaSource> mAudioSource;
    uint32_t mFlags;
    int64_t mBitrate;  // in bits/sec
    int32_t mNumChannels;
    int32_t mSampleRateHz;
    int64_t mTimeDelta;
    int64_t mDurationUsec;
    CacheStatus mCacheStatus;
    int64_t mSeekTimeMsec;
    int64_t mLastDecodedPositionUs;
    int16_t mCacheFill; // cache fill level in permille
    int16_t mLastNotifiedCacheFill; // last cache fill level communicated to the listener
    int16_t mCacheFillNotifThreshold; // threshold in cache fill level for cache fill to be reported
    AudioPlayback_Parameters mPlaybackParams;

    DataLocator mDataLocator;
    int         mDataLocatorType;

    notif_client_t mNotifyClient;
    void*          mNotifyUser;

    // mutex used for protecting the decode buffer
    Mutex       mDecodeBufferLock;
    // buffer passed from decoder to renderer
    MediaBuffer *mDecodeBuffer;

    // message handlers
    void onPrepare(const sp<AMessage> &msg);
    void onDecode();
    void onRender(const sp<AMessage> &msg);
    void onCheckCache(const sp<AMessage> &msg);
    void onNotify(const sp<AMessage> &msg);
    void onPlay();
    void onPause();
    void onSeek(const sp<AMessage> &msg);
    void onLoop(const sp<AMessage> &msg);

    CacheStatus getCacheRemaining(bool *eos);
    int64_t getPositionUsec();
    void reachedEndOfStream();
    void updatePlaybackParamsFromSource();
    void notifyStatus();
    void notifyCacheFill();
    void notifyPrepared(status_t prepareRes);
    void notify(const sp<AMessage> &msg, bool async);

    void resetDataLocator();

    DISALLOW_EVIL_CONSTRUCTORS(SfPlayer);
};

} // namespace android
