////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// THIS IS AUTO-GENERATED CODE.  PLEASE DO NOT EDIT (File bug reports against tools).
///
/// Auto-generated by: Parameter Parser V5.5.1 (2411131018)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  parametereztunecombase.h
/// @brief Auto-generated Chromatix parameter file
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef PARAMETEREZTUNECOMBASE_H
#define PARAMETEREZTUNECOMBASE_H

#include <atomic>
#include <condition_variable>
#include <thread>
#include <queue>
#include <chrono>
#include <mutex>
#include <list>

#include "parameterutils.h"

class EzTuneEvent
{
public:
    static constexpr INT32 CHECK_STATE = 0;
    static constexpr INT32 INDEFINITE  = -1;
    PARAMETER_INLINE EzTuneEvent(bool initialState = false)
    {
        m_state = initialState;
    }

    PARAMETER_INLINE void Set()
    {
        m_state = true;
        m_event.notify_all();
    }

    PARAMETER_INLINE void Reset()
    {
        m_state = false;
    }

    PARAMETER_INLINE bool Wait(INT32 timeoutMS)
    {
        if (!m_state && timeoutMS != CHECK_STATE)
        {
            std::unique_lock<std::mutex> lock(m_mutex);
            if (timeoutMS == INDEFINITE)
            {
                m_event.wait(lock);
            }
            else if (timeoutMS > CHECK_STATE)
            {
                auto wait = std::chrono::milliseconds(timeoutMS);
                m_event.wait_for(lock, wait);
            }
        }
        return m_state;
    }

private:
    bool m_state;
    std::condition_variable m_event;
    std::mutex m_mutex;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ParameterEzTuneComBase
///
/// @brief Ez Tune Worker
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class ParameterEzTuneComBase
{
public:
    static constexpr BYTE   const VERSION_MAJOR = 1;
    static constexpr BYTE   const VERSION_MINOR = 0;
    static constexpr INT32  const POLLING_TIMEOUT_MS = 100;
    static constexpr INT32  const RECV_TIMEOUT_SEC   = 3;
    static constexpr UINT32 const TAG_POS            = 0U;
    static constexpr UINT32 const TAG_LEN            = 2U;
    static constexpr UINT32 const CMD_POS            = TAG_POS + TAG_LEN;
    static constexpr UINT32 const CMD_LEN            = 2U;
    static constexpr UINT32 const LEN_POS            = CMD_POS + CMD_LEN;
    static constexpr UINT32 const LEN_LEN            = 2U;
    static constexpr UINT32 const ID_POS             = LEN_POS + LEN_LEN;
    static constexpr UINT32 const ID_LEN             = 4U;
    static constexpr UINT32 const PAY_POS            = ID_POS + ID_LEN;
    static constexpr BYTE   const CMD_MAX            = 9U; // ParameterEzTuneWorker::Commands::EZTUNE_DATA_MAX
    static constexpr UINT32 const MAX_PACKET_LEN     = 65535U;

    // External Port is ASCII QC in hex
    // Binary values are written in LSB order, this will appear as 0x43 0x51 in the packet
    static constexpr UINT16 const ExternalPort = 0x5143;
    static constexpr UINT16 const TAG          = ExternalPort;

    // Internal Port is used if opening an external socket generates a permissions error.
    // Using the internal port requires a gateway application running in user space to communicate with the tools.
    // Internal Port number is ASCII QC in decimal with 0x prepended to represent it as hex
    static constexpr UINT16 const InternalPort = 0x8167;

    struct Packet
    {
        PARAMETER_INLINE Packet(BYTE* packet, UINT32 len)
        {
            if (packet != nullptr && len > 0 && len < MAX_PACKET_LEN)
            {
                Length = len;
                Buffer = PARAMETER_NEW BYTE[len];
                PARAMETER_MEMCPY(Buffer, len, packet, len);
            }
            else
            {
                Buffer = nullptr;
                Length = 0;
            }
        }

        BYTE*  Buffer;
        UINT32 Length;
    };

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ParameterEzTuneComBase
    /// @brief Constructor
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ParameterEzTuneComBase(IParameterLoggingAPI* logAPI);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ~ParameterEzTuneComBase
    /// @brief Destructor
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    virtual ~ParameterEzTuneComBase();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Start
    /// @brief Starts the Ez Tune server
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    void Start();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// StopEzSpectraTune
    /// @brief Stops the Ez Tune server
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    void Stop();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Read
    ///
    /// Read() is not thread safe.  Do not call it from multiple threads
    ///
    /// @brief Read a com packet.  Blocking function.  Will return when a full packet is received or after Stop is called.
    /// @return The next Packet in the read queue, or nullptr if no packet is available before the timeout expires,
    ///         or Stop is called.  The returned Packet* must be deleted by the caller
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    Packet* Read(INT32 timeout);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Read
    ///
    /// Read() is not thread safe.  Do not call it from multiple threads
    ///
    /// @brief Reads com packet into the provided buffer
    /// @param buffer - Buffer to read into
    /// @param len    - Length of buffer to read into
    /// @ return number of bytes read or 0 if no packets received before the timeout,
    ///          or len is less than the received packet length (ensure length >= MAX_PACKET_LEN)
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    UINT32 Read(BYTE* buffer, UINT32 len, INT32 timeout);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Write
    ///
    /// Write() is not thread safe.  Do not call it from multiple threads
    ///
    /// @brief Queues a com packet for writing
    /// @param buffer - Buffer to write
    /// @param len    - Length of buffer to write
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    PARAMETER_INLINE void Write(BYTE* buffer, UINT32 len)
    {
        Write(PARAMETER_NEW Packet(buffer, len));
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Write
    ///
    /// Write() is not thread safe.  Do not call it from multiple threads
    ///
    /// @brief Queues a com packet for writing
    /// @param packet - Packet to write, this should be allocated using new as it will be deleted once it is sent
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    PARAMETER_INLINE void Write(Packet* packet)
    {
        m_writeMutex.lock();
        m_writeQueue.push(packet);
        m_writeEvent.Set();
        m_writeMutex.unlock();
    }

protected:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// StartCom
    /// @brief Starts the Ez Tune com server
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    virtual void StartCom() = 0;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// StopEzSpectraTune
    /// @brief Stops the Ez Tune com server
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    virtual void StopCom() = 0;

    static void ReadFunction(
        void* worker);
    static void WriteFunction(
        void* worker);
    void ReadWorker();
    void WriteWorker();

    INT32 ReceiveEzTuneRequest(
        BYTE*  packet,
        UINT32 packetSize,
        BYTE   commandMax,
        INT32  timeout,
        bool&  exit);

    virtual INT32 ReadData(
        BYTE*  packet,
        UINT32 packetSize,
        INT32  timeout,
        bool& exit) = 0;

    virtual INT32 WriteData(
        BYTE*  packet,
        UINT32 packetSize) = 0;

    BYTE*                 m_recvBuffer;
    UINT32                m_recvPos;
    IParameterLoggingAPI* m_logAPI;
    UINT64                m_serverID;
    bool                  m_exitData;
    EzTuneEvent           m_readEvent;
    EzTuneEvent           m_writeEvent;
    EzTuneEvent           m_exitEvent;
    UINT32                m_dataTimeout;
    std::thread*          m_readThread;
    std::thread*          m_writeThread;
    std::mutex            m_readMutex;
    std::mutex            m_writeMutex;
    std::queue<Packet*>   m_readQueue;
    std::queue<Packet*>   m_writeQueue;
};

#endif
