/*
 * Copyright (C) Texas Instruments - http://www.ti.com/
 *
 * 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 BUFFER_SOURCE_ADAPTER_H
#define BUFFER_SOURCE_ADAPTER_H

#ifdef OMAP_ENHANCEMENT_CPCAM

#include "CameraHal.h"
#include <ui/GraphicBufferMapper.h>
#include <hal_public.h>

namespace Ti {
namespace Camera {

/**
 * Handles enqueueing/dequeing buffers to tap-in/tap-out points
 * TODO(XXX): this class implements DisplayAdapter for now
 * but this will most likely change once tap-in/tap-out points
 * are better defined
 */

class BufferSourceAdapter : public DisplayAdapter
{
// private types
private:
    ///Constant declarations
    static const int NO_BUFFERS_IMAGE_CAPTURE_SYSTEM_HEAP;


    // helper class to return frame in different thread context
    class ReturnFrame : public android::Thread {
    public:
        ReturnFrame(BufferSourceAdapter* __this) : mBufferSourceAdapter(__this) {
            android::AutoMutex lock(mReturnFrameMutex);
            mDestroying = false;
            mFrameCount = 0;
        }

        ~ReturnFrame() {
            android::AutoMutex lock(mReturnFrameMutex);
         }

        void signal() {
            android::AutoMutex lock(mReturnFrameMutex);
            mFrameCount++;
            mReturnFrameCondition.signal();
        }

        virtual void requestExit() {
            Thread::requestExit();

            android::AutoMutex lock(mReturnFrameMutex);
            mDestroying = true;
            mReturnFrameCondition.signal();
        }

        virtual bool threadLoop() {
            android::AutoMutex lock(mReturnFrameMutex);
            if ( 0 >= mFrameCount ) {
                mReturnFrameCondition.wait(mReturnFrameMutex);
            }
            if (!mDestroying) {
                mBufferSourceAdapter->handleFrameReturn();
                mFrameCount--;
            }
            return true;
        }

    private:
        BufferSourceAdapter* mBufferSourceAdapter;
        android::Condition mReturnFrameCondition;
        android::Mutex mReturnFrameMutex;
        int mFrameCount;
        bool mDestroying;
    };

    // helper class to queue frame in different thread context
    class QueueFrame : public android::Thread {
    public:
        QueueFrame(BufferSourceAdapter* __this) : mBufferSourceAdapter(__this) {
            mDestroying = false;
        }

        ~QueueFrame() {
         }

        void addFrame(CameraFrame *frame) {
            android::AutoMutex lock(mFramesMutex);
            mFrames.add(new CameraFrame(*frame));
            mFramesCondition.signal();
        }

        virtual void requestExit() {
            Thread::requestExit();

            mDestroying = true;

            android::AutoMutex lock(mFramesMutex);
            while (!mFrames.empty()) {
                CameraFrame *frame = mFrames.itemAt(0);
                mFrames.removeAt(0);
                frame->mMetaData.clear();
                delete frame;
            }
            mFramesCondition.signal();
        }

        virtual bool threadLoop() {
            CameraFrame *frame = NULL;
            {
                android::AutoMutex lock(mFramesMutex);
                while (mFrames.empty() && !mDestroying) mFramesCondition.wait(mFramesMutex);
                if (!mDestroying) {
                    frame = mFrames.itemAt(0);
                    mFrames.removeAt(0);
                }
            }

            if (frame) {
                mBufferSourceAdapter->handleFrameCallback(frame);
                frame->mMetaData.clear();

                // signal return frame thread that it can dequeue a buffer now
                mBufferSourceAdapter->mReturnFrame->signal();

                delete frame;
            }

            return true;
        }

    private:
        BufferSourceAdapter* mBufferSourceAdapter;
        android::Vector<CameraFrame *> mFrames;
        android::Condition mFramesCondition;
        android::Mutex mFramesMutex;
        bool mDestroying;
    };

    enum {
        BUFFER_SOURCE_TAP_IN,
        BUFFER_SOURCE_TAP_OUT
    };

// public member functions
public:
    BufferSourceAdapter();
    virtual ~BufferSourceAdapter();

    virtual status_t initialize();
    virtual int setPreviewWindow(struct preview_stream_ops *source);
    virtual int setFrameProvider(FrameNotifier *frameProvider);
    virtual int setErrorHandler(ErrorNotifier *errorNotifier);
    virtual int enableDisplay(int width, int height, struct timeval *refTime = NULL);
    virtual int disableDisplay(bool cancel_buffer = true);
    virtual status_t pauseDisplay(bool pause);
#if PPM_INSTRUMENTATION || PPM_INSTRUMENTATION_ABS
    // Not implemented in this class
    virtual status_t setSnapshotTimeRef(struct timeval *refTime = NULL) { return NO_ERROR; }
#endif
    virtual bool supportsExternalBuffering();
    virtual CameraBuffer * allocateBufferList(int width, int dummyHeight, const char* format, int &bytes, int numBufs);
    virtual CameraBuffer *getBufferList(int *numBufs);
    virtual uint32_t * getOffsets() ;
    virtual int getFd() ;
    virtual int freeBufferList(CameraBuffer * buflist);
    virtual int maxQueueableBuffers(unsigned int& queueable);
    virtual int minUndequeueableBuffers(int& unqueueable);
    virtual bool match(const char * str);

    virtual CameraBuffer * getBuffers(bool reset = false);
    virtual unsigned int getSize();
    virtual int getBufferCount();

    static void frameCallback(CameraFrame* caFrame);
    void addFrame(CameraFrame* caFrame);
    void handleFrameCallback(CameraFrame* caFrame);
    bool handleFrameReturn();

private:
    void destroy();
    status_t returnBuffersToWindow();

private:
    preview_stream_ops_t*  mBufferSource;
    FrameProvider *mFrameProvider; // Pointer to the frame provider interface

    mutable android::Mutex mLock;
    int mBufferCount;
    CameraBuffer *mBuffers;

    android::KeyedVector<buffer_handle_t *, int> mFramesWithCameraAdapterMap;
    android::sp<ErrorNotifier> mErrorNotifier;
    android::sp<ReturnFrame> mReturnFrame;
    android::sp<QueueFrame> mQueueFrame;

    uint32_t mFrameWidth;
    uint32_t mFrameHeight;
    uint32_t mPreviewWidth;
    uint32_t mPreviewHeight;

    int mBufferSourceDirection;

    const char *mPixelFormat;
};

} // namespace Camera
} // namespace Ti

#endif

#endif