#ifndef _XECALLQUEUE_HPP
#define _XECALLQUEUE_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program Test Executor
 * ------------------------------------------
 *
 * Copyright 2014 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.
 *
 *//*!
 * \file
 * \brief Cross-thread function call dispatcher.
 *//*--------------------------------------------------------------------*/

#include "xeDefs.hpp"
#include "deMutex.hpp"
#include "deSemaphore.hpp"
#include "deRingBuffer.hpp"

#include <vector>

namespace xe
{

class Call;
class CallReader;
class CallWriter;
class CallQueue;

// \todo [2012-07-10 pyry] Optimize memory management in Call
// \todo [2012-07-10 pyry] CallQueue API could be improved to match TestLog API more closely.
//                           In order to do that, reference counting system for call object management is needed.

class Call
{
public:
    typedef void (*Function)(CallReader &data);

    Call(void);
    ~Call(void);

    void clear(void);

    Function getFunction(void) const
    {
        return m_func;
    }
    void setFunction(Function func)
    {
        m_func = func;
    }

    size_t getDataSize(void) const
    {
        return m_data.size();
    }
    void setDataSize(size_t size)
    {
        m_data.resize(size);
    }

    const uint8_t *getData(void) const
    {
        return m_data.empty() ? DE_NULL : &m_data[0];
    }
    uint8_t *getData(void)
    {
        return m_data.empty() ? DE_NULL : &m_data[0];
    }

private:
    Function m_func;
    std::vector<uint8_t> m_data;
};

class CallReader
{
public:
    CallReader(Call *call);
    CallReader(void) : m_call(DE_NULL), m_curPos(0)
    {
    }

    void read(uint8_t *bytes, size_t numBytes);
    const uint8_t *getDataBlock(size_t numBytes); //!< \note Valid only during call.
    bool isDataConsumed(void) const;              //!< all data has been consumed

private:
    CallReader(const CallReader &other);            //!< disallowed
    CallReader &operator=(const CallReader &other); //!< disallowed

    Call *m_call;
    size_t m_curPos;
};

class CallWriter
{
public:
    CallWriter(CallQueue *queue, Call::Function function);
    ~CallWriter(void);

    void write(const uint8_t *bytes, size_t numBytes);
    void enqueue(void);

private:
    CallWriter(const CallWriter &other);
    CallWriter &operator=(const CallWriter &other);

    CallQueue *m_queue;
    Call *m_call;
    bool m_enqueued;
};

class CallQueue
{
public:
    CallQueue(void);
    ~CallQueue(void);

    void callNext(void); //!< Executes and removes first call in queue. Will block if queue is empty.

    Call *getEmptyCall(void);
    void enqueue(Call *call);
    void freeCall(Call *call);
    void cancel(void);

private:
    CallQueue(const CallQueue &other);
    CallQueue &operator=(const CallQueue &other);

    bool m_canceled;
    de::Semaphore m_callSem;

    de::Mutex m_lock;
    std::vector<Call *> m_calls;
    std::vector<Call *> m_freeCalls;
    de::RingBuffer<Call *> m_callQueue;
};

// Stream operators for call reader / writer.

CallReader &operator>>(CallReader &reader, std::string &value);
CallWriter &operator<<(CallWriter &writer, const char *str);

template <typename T>
CallReader &operator>>(CallReader &reader, T &value)
{
    reader.read((uint8_t *)&value, sizeof(T));
    return reader;
}

template <typename T>
CallWriter &operator<<(CallWriter &writer, T &value)
{
    writer.write((const uint8_t *)&value, sizeof(T));
    return writer;
}

} // namespace xe

#endif // _XECALLQUEUE_HPP
