////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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  parametereztunecom.cpp
/// @brief Auto-generated Chromatix parameter file
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "parametereztunecom.h"
#include "parametertypes.h"

#ifdef _WIN32
#define PARAMETER_POLL WSAPoll
#else
#define PARAMETER_POLL poll
#include <arpa/inet.h>
#endif // _WIN32

// Set this flag to true to use the Internal port numbers by default
// Can be used to test the gateway application in a Windows unit test environment
bool ParameterEzTuneCom::UseInternalPort = false;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ParameterEzTuneCom::SocketID
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterEzTuneCom::SocketID::CloseSocket()
{
    if (IsOpen())
    {
        CloseConnection();
#ifdef _WIN32
        closesocket(ID);
#else
        close(ID);
#endif // _WIN32

        Initialize();
    }
}

void ParameterEzTuneCom::SocketID::CloseConnection()
{
    if (IsConnected())
    {
#ifdef _WIN32
        closesocket(Connection);
#else
        close(Connection);
#endif // _WIN32

        Connection = PARAMETER_INVALID_SOCKET;
    }
}

INT32 ParameterEzTuneCom::SocketID::ReadData(
    BYTE*  packet,
    UINT32 packetSize,
    INT32  timeout,
    bool&  exit)
{
    INT32  result = 0;
    time_t startTime;
    time_t currTime;
    time(&startTime);

    struct pollfd pfd;
    pfd.events = POLLIN;
    INT32 pollResult = 0;

    do
    {
        if (!IsConnected())
        {
            pfd.fd = ID;
            pollResult = PARAMETER_POLL(&pfd, 1, POLLING_TIMEOUT_MS);

            if (pollResult > 0)
            {
                SOCKET_LEN addrlen = sizeof(ConnectAddr);
                Connection = accept(ID, (struct sockaddr*)&ConnectAddr, &addrlen);
                PARAMETER_LOG_DEBUG(LogAPI, "EzTune accepted data connection on port %s\n", ConnectAddr.PortName);
            }
        }
        else
        {
            pfd.fd = Connection;
            pollResult = PARAMETER_POLL(&pfd, 1, POLLING_TIMEOUT_MS);
        }

        if (pollResult > 0)
        {
            result = recv(Connection, (CHAR*)packet, packetSize, 0);

            if (result <= 0)
            {
                PARAMETER_LOG_DEBUG(LogAPI, "EzTune client disconnected.  Resetting socket\n");
                CloseConnection();
                result = -1;
            }
        }

        time(&currTime);
    } while (result == 0 && !exit && (timeout < 0 || difftime(currTime, startTime) < timeout));

    return result;
}

INT32 ParameterEzTuneCom::SocketID::WriteData(
    BYTE*  packet,
    UINT32 packetSize)
{
    return send(Connection, (const CHAR*)packet, (INT32)packetSize, 0);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ParameterEzTuneCom::SocketAddress
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterEzTuneCom::SocketAddress::SetupSocket(AddrType type, UINT16 port)
{
#ifdef _WIN32
    bool windows = true;
#else
    bool windows = false;
#endif

    Port = port;
    if (type == AddrType::External || windows)
    {
        // Windows does not support AF_UNIX socket type, so used External instead for external ports
        Type = AddrType::External;
        PARAMETER_SPRINTF(PortName, SUN_UNIX_PATH_MAX, "0x%x", port);
        Address.Sin.sin_port        = htons(port);
        Address.Sin.sin_family      = AF_INET;
        Address.Sin.sin_addr.s_addr = INADDR_ANY;
        Length = sizeof(struct sockaddr_in);
    }
    else
    {
        Type = AddrType::Internal;
        PARAMETER_SPRINTF(PortName, SUN_UNIX_PATH_MAX, "#EzTune%x", port);
        PARAMETER_STRCPY(Address.Sun.sun_path, SUN_UNIX_PATH_MAX, PortName);
        Address.Sun.sun_path[0] = 0; // Set port as abstract (first byte is 0)
        Address.Sun.sun_family  = AF_UNIX;
        Length = (socklen_t)(PARAMETER_STRLEN(PortName) + 1 + sizeof(Address.Sun.sun_family));
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ParameterEzTuneCom
/// @brief Constructor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ParameterEzTuneCom::ParameterEzTuneCom(IParameterLoggingAPI* logAPI):
    ParameterEzTuneComBase(logAPI)
{
    m_initSockets   = true;
    m_dataID.LogAPI = logAPI;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Start
/// @brief Starts the Ez Tune com server
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterEzTuneCom::StartCom()
{
    if (m_initSockets)
    {
        m_initSockets = !InitSockets();
    }

    OpenPort();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// StopEzSpectraTune
/// @brief Stops the Ez Tune com server
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterEzTuneCom::StopCom()
{
    ClosePort();
}

bool ParameterEzTuneCom::InitSockets()
{
    bool result = true;

#ifdef _WIN32
    WSADATA wsaData;
    INT32 WSAresult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (WSAresult != NO_ERROR)
    {
        PARAMETER_LOG_ERROR(m_logAPI, "OpenSocket failed WSAStartup error: %d\n", WSAresult);
        result = false;
    }

#else
#endif // _WIN32

    return result;
}

void ParameterEzTuneCom::CloseSocket(SocketID& sockID, bool term)
{
    sockID.CloseSocket();
#ifdef _WIN32
    if (term)
    {
        WSACleanup();
    }
#endif // _WIN32
}

void ParameterEzTuneCom::CloseConnection(SocketID& sockID)
{
    sockID.CloseConnection();
}

void ParameterEzTuneCom::LogDebug(const CHAR* message, const SocketID& socket, const CHAR* portName, INT32 result, INT32 error)
{
#ifdef _WIN32
    PARAMETER_LOG_DEBUG(m_logAPI, "%s, Socket: %llu, Port: %s: Result: %d, errno: %d\n", message,
        socket.ID, portName, result, error);
#else
    PARAMETER_LOG_DEBUG(m_logAPI, "%s, Socket: %d, Port: %s: Result: %d, errno: %d\n", message,
        socket.ID, portName, result, error);
#endif
}

void ParameterEzTuneCom::LogError(const CHAR* message, const SocketID& socket, const CHAR* portName, INT32 result, INT32 error)
{
#ifdef _WIN32
    PARAMETER_LOG_ERROR(m_logAPI, "%s, Socket: %llu, Port: %s: Result: %d, errno: %d\n", message,
        socket.ID, portName, result, error);
#else
    PARAMETER_LOG_ERROR(m_logAPI, "%s, Socket: %d, Port: %s: Result: %d, errno: %d\n", message,
        socket.ID, portName, result, error);
#endif
}

UINT16 ParameterEzTuneCom::OpenPort()
{
    if (!m_dataID.IsOpen())
    {
        constexpr UINT16 MAX_PORTS = 64;
        constexpr INT32  BACKLOG = 5;
        INT32  bindResult = 0;
        INT32  error      = 0;
        bool   connected  = false;

        UINT16 port = UseInternalPort ? InternalPort : ExternalPort;

        // Create INET socket
        m_dataID.SocketAddr.SetupSocket(SocketAddress::AddrType::External, port);
        m_dataID.ConnectAddr.SetupSocket(SocketAddress::AddrType::External, port);

        m_dataID.ID = socket(AF_INET, SOCK_STREAM, 0);

        error = GetError();

        if (m_dataID.ID != PARAMETER_INVALID_SOCKET)
        {
            // Create INET socket succeeded
            LogDebug("EzTune Server INET socket created", m_dataID, m_dataID.SocketAddr.PortName, 0, 0);
        }
        else
        {
            // Create INET socket failed
            LogDebug("EzTune Server INET socket create failed", m_dataID, m_dataID.SocketAddr.PortName, 0, error);

            if (!UseInternalPort)
            {
                // Create UNIX socket
                m_dataID.SocketAddr.SetupSocket(SocketAddress::AddrType::Internal, InternalPort);
                m_dataID.ConnectAddr.SetupSocket(SocketAddress::AddrType::Internal, InternalPort);

                m_dataID.ID = socket(AF_UNIX, SOCK_STREAM, 0);

                error = GetError();

                if (m_dataID.ID != PARAMETER_INVALID_SOCKET)
                {
                    // Create UNIX socket succeeded
                    LogDebug("EzTune Server UNIX socket created", m_dataID, m_dataID.SocketAddr.PortName, 0, 0);
                }
                else
                {
                    // Create UNIX socket failed
                    LogError("EzTune Server UNIX socket create failed", m_dataID, m_dataID.SocketAddr.PortName, 0, error);
                }
            }
        }

        if (m_dataID.ID != PARAMETER_INVALID_SOCKET)
        {
            UINT16 maxPort = m_dataID.SocketAddr.Port + MAX_PORTS;

            do
            {
                // Find available TCPIP port
                bindResult = bind(m_dataID.ID, (struct sockaddr*)&m_dataID.SocketAddr.Address, m_dataID.SocketAddr.Length);

                connected = bindResult >= 0;

                error = GetError();

                if (connected)
                {
                    listen(m_dataID.ID, BACKLOG);
                }
                else
                {
                    LogDebug("EzTune Server bind failed", m_dataID, m_dataID.SocketAddr.PortName, bindResult, error);
                    m_dataID.SocketAddr.IncrementPort();
                }
            } while (!connected && m_dataID.SocketAddr.Port < maxPort);
        }

        if (connected)
        {
            LogDebug("EzTune Server bound", m_dataID, m_dataID.SocketAddr.PortName, bindResult, 0);
        }
        else if (m_dataID.ID == PARAMETER_INVALID_SOCKET)
        {
            LogError("EzTune Server socket create failed", m_dataID, m_dataID.SocketAddr.PortName, bindResult, error);
        }
        else
        {
            PARAMETER_LOG_ERROR(m_logAPI, "EzTune Failed to bind any ports\n");
            CloseSocket(m_dataID, true);
        }
    }

    return m_dataID.SocketAddr.Port;
}

void ParameterEzTuneCom::ClosePort()
{
    m_dataID.CloseSocket();
}

INT32 ParameterEzTuneCom::ReadData(
    BYTE*  packet,
    UINT32 packetSize,
    INT32  timeout,
    bool&  exit)
{
    return m_dataID.ReadData(packet, packetSize, timeout, exit);
}

INT32 ParameterEzTuneCom::WriteData(
    BYTE*  packet,
    UINT32 packetSize)
{
    return m_dataID.WriteData(packet, packetSize);
}
