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

#include "parametereztune.h"
#include "parametereztuneworker.h"
#include "parametertypes.h"

ParameterEzTuneWorker::ParameterEzTuneWorker(IParameterDataAPI* dataAPI)
{
    m_dataAPI  = dataAPI;
    m_com         = PARAMETER_NEW ParameterEzTuneCom(dataAPI);
    m_exitData    = false;
    m_dataTimeout = RECV_TIMEOUT_SEC;
    m_readThread  = nullptr;
    m_callback    = nullptr;

    if (m_dataAPI == nullptr)
    {
        PARAMETER_LOG_ERROR(m_dataAPI, "ParameterEzTuneWorker constructed with a nullptr Set Manager object\n");
    }
}

ParameterEzTuneWorker::~ParameterEzTuneWorker()
{
    PARAMETER_DELETE m_com;
    m_com = nullptr;
}

void ParameterEzTuneWorker::Start()
{
    if (m_readThread == nullptr)
    {
        m_com->Start();
        m_readThread = PARAMETER_NEW std::thread(&ReadFunction, this);
    }
}

void ParameterEzTuneWorker::Stop()
{
    m_exitData = true;

    if (m_readThread != nullptr)
    {
        m_com->Stop();

        if (m_readThread->joinable())
        {
            // Wait for the read thread to exit, this can take up to POLLING_TIMEOUT_MS milliseconds
            m_readThread->join();
        }

        PARAMETER_DELETE m_readThread;
        m_readThread = nullptr;
    }
}

void ParameterEzTuneWorker::ReadFunction(void* worker)
{
    if (worker != nullptr)
    {
        ((ParameterEzTuneWorker*)worker)->ReadWorker();
    }
}

void ParameterEzTuneWorker::ReadWorker()
{
    do
    {
        ParameterEzTuneCom::Packet* request = m_com->Read(m_dataTimeout);

        if (!m_exitData && request != nullptr && request->Length > 0)
        {
            UINT16 cmd    = 0;
            UINT32 cmdPos = CMD_POS;
            ParameterUtils::Read(&cmd, request->Buffer, request->Length, &cmdPos, 1, m_dataAPI);
            switch ((Commands)cmd)
            {
            case Commands::EZTUNE_DEVICE_INFO:
                ProcessDeviceInfo();
                break;
            case Commands::EZTUNE_DATA_ACTIVE_MODES:
                ProcessDataActiveModes();
                break;
            case Commands::EZTUNE_DATA_CTRL_VALUES:
                ProcessDataControlValues();
                break;
            case Commands::EZTUNE_DATA_MODES:
                ProcessDataModes(request->Buffer, request->Length);
                break;
            case Commands::EZTUNE_DATA_INFO:
                ProcessDataInfo(request->Buffer, request->Length);
                break;
            case Commands::EZTUNE_DATA_GET_VALUES:
                ProcessDataGetValues(request->Buffer, request->Length);
                break;
            case Commands::EZTUNE_DATA_SET_VALUES:
                ProcessDataSetValues(request->Buffer, request->Length);
                break;
            case Commands::EZTUNE_DATA_TRIG_VALUES:
                ProcessDataTriggerValues();
                break;
            case Commands::EZTUNE_CUSTOM:
                ProcessCustom(request->Buffer, request->Length);
                break;
            default:
                break;
            }
        }
    } while (!m_exitData);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ProcessDataDeviceInfo
///
/// @brief Process Data Device Info:
///        For sockets based comminication, file based uses a different method defined in ParameterEzTuneCom
///
/// Device info Request: "<Command ID>"
/// Device info Resonse: "<Command ID><16 bit Length><EzTune version 1 byte major><1 byte minor><device name>"
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterEzTuneWorker::ProcessDeviceInfo()
{
    const UINT32 MAJOR_POS = ID_POS;
    const UINT32 MINOR_POS = MAJOR_POS + 1;
    const UINT32 DI_ID_POS = MINOR_POS + 1;
    const UINT32 NAME_POS  = DI_ID_POS + 8;
    const UINT32 MAX_SIZE  = 256;
    BYTE response[MAX_SIZE];
    UINT32 pos = 0;

    // Write the Command and Tag
    ParameterUtils::Write(ParameterEzTuneCom::TAG, response, MAX_SIZE, &pos, 1, m_dataAPI);
    ParameterUtils::Write((UINT16)Commands::EZTUNE_DEVICE_INFO, response, MAX_SIZE, &pos, 1, m_dataAPI);

    // Write the version
    response[MAJOR_POS] = VERSION_MAJOR;
    response[MINOR_POS] = VERSION_MINOR;

    // Write the EzTune ID
    pos = DI_ID_POS;
    ParameterUtils::Write(reinterpret_cast<UINT64>(m_dataAPI), response, MAX_SIZE, &pos, 1, m_dataAPI);

    // Write the Binary Tag, if not available will write the EzTune ID in hex string format
    response[NAME_POS]  = 0;
    if (m_dataAPI->GetLoggingIdentifier() != nullptr && m_dataAPI->GetLoggingIdentifier()[0] != '\0')
    {
        PARAMETER_STRCPY((CHAR*)&response[NAME_POS], MAX_SIZE - NAME_POS, m_dataAPI->GetLoggingIdentifier());
    }
    else
    {
#ifdef _WIN32
        PARAMETER_SPRINTF((CHAR*) & response[NAME_POS], MAX_SIZE - NAME_POS, "0x%016llx", reinterpret_cast<SIZE_T>(m_dataAPI));
#else
        PARAMETER_SPRINTF((CHAR*)&response[NAME_POS], MAX_SIZE - NAME_POS, "0x%016lx", reinterpret_cast<SIZE_T>(m_dataAPI));
#endif
    }

    UINT16 rspLen = (UINT16)(NAME_POS + PARAMETER_STRLEN((CHAR*)&response[NAME_POS]) + 1);

    // Write the length
    pos = LEN_POS;
    ParameterUtils::Write(rspLen, response, MAX_SIZE, &pos, 1, m_dataAPI);

    m_com->Write(response, rspLen);

    constexpr INT32 MAX_PRINT = 128;
    CHAR printPayload[MAX_PRINT];
    SIZE_T len = PARAMETER_STRLEN((CHAR*)&response[NAME_POS]);
    if (len >= MAX_PRINT)
    {
        len = MAX_PRINT - 1;
    }
    PARAMETER_MEMCPY(printPayload, len, &response[NAME_POS], len);
    printPayload[len] = '\0';

    PARAMETER_LOG_DEBUG(m_dataAPI, "EzTune queued command: Info(%d), Version: %u.%u, Name: %s\n",
        response[CMD_POS], VERSION_MAJOR, VERSION_MINOR, printPayload);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ProcessDataActiveModes
///
/// @brief Process Active Modes
///
/// Active modes branch Request:  "<Command ID>"
/// Active modes branch Response: "<Command ID><nul terminated pipe '|' separated mode branch "Mode.SubMode">;..."
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterEzTuneWorker::ProcessDataActiveModes()
{
    const UINT32 MAX_SIZE = 1024;
    BYTE response[MAX_SIZE];
    UINT32 pos = 0;

    // Write the Command and Tag
    ParameterUtils::Write(ParameterEzTuneCom::TAG, response, MAX_SIZE, &pos, 1, m_dataAPI);
    ParameterUtils::Write((UINT16)Commands::EZTUNE_DATA_ACTIVE_MODES, response, MAX_SIZE, &pos, 1, m_dataAPI);

    response[ID_POS]  = 0; // This packet does not have an ID, so payload starts at ID_POS
    m_dataAPI->GetActiveModes((CHAR*)&response[ID_POS], MAX_SIZE - ID_POS);

    UINT16 rspLen = (UINT16)(ID_POS + PARAMETER_STRLEN((CHAR*)&response[ID_POS]) + 1);

    // Write the length
    pos = LEN_POS;
    ParameterUtils::Write(rspLen, response, MAX_SIZE, &pos, 1, m_dataAPI);

    m_com->Write(response, rspLen);

    constexpr INT32 MAX_PRINT = 128;
    CHAR printPayload[MAX_PRINT];
    SIZE_T len = PARAMETER_STRLEN((CHAR*)&response[ID_POS]);
    if (len >= MAX_PRINT)
    {
        len = MAX_PRINT - 1;
    }
    PARAMETER_MEMCPY(printPayload, len, &response[ID_POS], len);
    printPayload[len] = '\0';

    PARAMETER_LOG_DEBUG(m_dataAPI, "EzTune queued command: Active Modes(%d), Version: %u.%u, Active: %s\n",
        response[CMD_POS], VERSION_MAJOR, VERSION_MINOR, printPayload);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ProcessDataControlValues
///
/// @brief Process Control Values Request
///
/// Current control values Request:  "<Command ID>"
/// Current control values Response: "<Command ID><nul terminated comma separated list of all values "Control Enum Value=Value">"
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterEzTuneWorker::ProcessDataControlValues()
{
    const UINT32 MAX_SIZE = 4096;
    BYTE response[MAX_SIZE];
    UINT32 pos = 0;

    // Write the Command and Tag
    ParameterUtils::Write(ParameterEzTuneCom::TAG, response, MAX_SIZE, &pos, 1, m_dataAPI);
    ParameterUtils::Write((UINT16)Commands::EZTUNE_DATA_CTRL_VALUES, response, MAX_SIZE, &pos, 1, m_dataAPI);

    response[ID_POS] = 0; // This packet does not have an ID, so payload starts at ID_POS
    m_dataAPI->GetControlValues((CHAR*)&response[ID_POS], MAX_SIZE - ID_POS);

    UINT16 rspLen = (UINT16)(ID_POS + PARAMETER_STRLEN((CHAR*)&response[ID_POS]) + 1);

    // Write the length
    pos = LEN_POS;
    ParameterUtils::Write(rspLen, response, MAX_SIZE, &pos, 1, m_dataAPI);

    m_com->Write(response, rspLen);

    constexpr INT32 MAX_PRINT = 128;
    CHAR printPayload[MAX_PRINT];
    SIZE_T len = PARAMETER_STRLEN((CHAR*)&response[ID_POS]);
    if (len >= MAX_PRINT)
    {
        len = MAX_PRINT - 1;
    }
    PARAMETER_MEMCPY(printPayload, len, &response[ID_POS], len);
    printPayload[len] = '\0';

    PARAMETER_LOG_DEBUG(m_dataAPI, "EzTune queued command: Control Values(%d), Version: %u.%u, Name: %s\n",
        response[CMD_POS], VERSION_MAJOR, VERSION_MINOR, printPayload);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ProcessDataTriggerValues
///
/// @brief Process Trigger Values Request
///
/// Current trigger values Request:  "<Command ID>"
/// Current trigger values Response: "<Command ID><nul terminated comma separated list of all values "Trigger Enum Value=Value">"
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterEzTuneWorker::ProcessDataTriggerValues()
{
    const UINT32 MAX_SIZE = 4096;
    BYTE response[MAX_SIZE];
    UINT32 pos = 0;

    // Write the Command and Tag
    ParameterUtils::Write(ParameterEzTuneCom::TAG, response, MAX_SIZE, &pos, 1, m_dataAPI);
    ParameterUtils::Write((UINT16)Commands::EZTUNE_DATA_TRIG_VALUES, response, MAX_SIZE, &pos, 1, m_dataAPI);

    response[ID_POS] = 0; // This packet does not have an ID, so payload starts at ID_POS
    m_dataAPI->GetTriggerValues((CHAR*)&response[ID_POS], MAX_SIZE - ID_POS);

    UINT16 rspLen = (UINT16)(ID_POS + PARAMETER_STRLEN((CHAR*)&response[ID_POS]) + 1);

    // Write the length
    pos = LEN_POS;
    ParameterUtils::Write(rspLen, response, MAX_SIZE, &pos, 1, m_dataAPI);

    m_com->Write(response, rspLen);

    constexpr INT32 MAX_PRINT = 128;
    CHAR printPayload[MAX_PRINT];
    SIZE_T len = PARAMETER_STRLEN((CHAR*)&response[ID_POS]);
    if (len >= MAX_PRINT)
    {
        len = MAX_PRINT - 1;
    }
    PARAMETER_MEMCPY(printPayload, len, &response[ID_POS], len);
    printPayload[len] = '\0';

    PARAMETER_LOG_DEBUG(m_dataAPI, "EzTune queued command: Trigger Values(%d), Version: %u.%u, Name: %s\n",
        response[CMD_POS], VERSION_MAJOR, VERSION_MINOR, printPayload);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ProcessDataModes
///
/// @brief Process Data Modes
///
/// Data Modes command Request: "<Command ID><32 bit Request ID><nul terminated pipe separated mode branch>"
/// Data Modes command Request: "<Command ID><32 bit Request ID><nul terminated comma separated list of child modes>"
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterEzTuneWorker::ProcessDataModes(
    BYTE*  request,
    UINT32 length)
{
    const UINT32 MAX_SIZE = 4096;
    const UINT32 SUB_SIZE = 512;

    if (length > PAY_POS)
    {
        BYTE response[MAX_SIZE];
        CHAR  shorthand[SUB_SIZE];
        UINT32 pos = ID_POS;
        UINT32 id  = 0;

        // Get ID for the log message
        ParameterUtils::Read(&id, request, length, &pos, 1, m_dataAPI);

        // Copy Tag, Command, Length, and Request ID from the request, Length will be overwritten
        PARAMETER_MEMCPY(response, MAX_SIZE, request, PAY_POS);
        response[PAY_POS] = 0;

        m_dataAPI->GetModeShorthand((CHAR*)&request[pos], shorthand, SUB_SIZE);
        UINT16 rspLen = (UINT16)(m_dataAPI->GetChildModes(shorthand, &response[PAY_POS], MAX_SIZE - PAY_POS) + PAY_POS);

        // Write the length
        UINT32 len_pos = LEN_POS;
        ParameterUtils::Write(rspLen, response, MAX_SIZE, &len_pos, 1, m_dataAPI);

        m_com->Write(response, rspLen);

        constexpr INT32 MAX_PRINT = 128;
        CHAR printPayload[MAX_PRINT];
        SIZE_T len = PARAMETER_STRLEN((CHAR*)&response[PAY_POS]);
        if (len >= MAX_PRINT)
        {
            len = MAX_PRINT - 1;
        }
        PARAMETER_MEMCPY(printPayload, len, &response[PAY_POS], len);
        printPayload[len] = '\0';

        PARAMETER_LOG_DEBUG(m_dataAPI, "EzTune queued command: Modes(%d), ID: %u, Payload: %s...\n",
            response[CMD_POS], id , printPayload);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ProcessDataInfo
///
/// @brief Process Data Info
///
/// Data Info command Request: "<Command ID><32 bit Request ID><pipe separated mode branch>[:<variable path>]\0"
/// Only a single structure may be requested at a time.  If no structure is specified, all modules will be returned
/// for the specified mode
/// Module Example:   "<Command ID><32 bit Request ID>Default|Sensor.2|Usecase.Snapshot|Feature1.ISPHDR\0"
/// Variable Example: "<Command ID><32 bit Request ID>Default|Sensor.2|Usecase.Snapshot|Feature1.ISPHDR:cc14_ipe_v2.chromatix_cc14_reserve\0"
/// Variable Example: "<Command ID><32 bit Request ID>Default|Sensor.2|Usecase.Snapshot|Feature1.ISPHDR:cc14_ipe_v2.chromatix_cc14_reserve.ai_mod_blend_lpf_tab\0"
///
/// Data Info command Response: "<Command ID><32 bit Request ID><semi-colon separated list of info>\0"
/// Info: "[<Name>],<Type>[,<element count>[,<1 is optional>]]\0"
/// Name: Name of the variable, or empty if the variable path ends with the entry name
/// Type: Valiable type
/// Element Count: Number of elements in the array (can be 0), included only if the variable is an array
/// Is Optional: 1 if the variable is an optional parameter, missing if not an optional parameter
/// Module Example:  "<Command ID><32 bit Request ID>abf40_bps_v2,Module;anr14_ipe_v2,Module;cc14_ipe_v2,Module"
/// Varable Example: "<Command ID><32 bit Request ID>q_factor,UInt;ai_mod_blend_lpf_tab,Structure;"
/// Varable Example: "<Command ID><32 bit Request ID>ai_mod_blend_lpf,UInt,4\0"
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterEzTuneWorker::ProcessDataInfo(
    BYTE*  request,
    UINT32 length)
{
    const UINT32 MAX_SIZE = 65535;
    const UINT32 SUB_SIZE = 512;
    if (length > PAY_POS)
    {
        BYTE* response = PARAMETER_NEW BYTE[MAX_SIZE];

        if (response != nullptr)
        {
            CHAR* structure = nullptr;
            CHAR  mode[SUB_SIZE];
            CHAR  shorthand[SUB_SIZE];
            PList<ParameterParserInfo> info;
            UINT32 id  = 0;
            UINT32 pos = ID_POS;

            // Get ID for the log message
            ParameterUtils::Read(&id, request, length, &pos, 1, m_dataAPI);

            // Copy Tag, Command, Length, and Request ID from the request, Length will be overwritten
            PARAMETER_MEMCPY(response, MAX_SIZE, request, PAY_POS);
            response[PAY_POS] = 0;

            bool result = false;
            pos = PAY_POS;
            pos = ParameterUtils::GetSubString((CHAR*)&request[pos], ':', mode, SUB_SIZE) + pos;
            pos++; // skip colon if present
            if (pos + 1 < length)
            {
                structure = (CHAR*)&request[pos];
            }

            m_dataAPI->GetModeShorthand(mode, shorthand, SUB_SIZE);

            result = m_dataAPI->GetInfo(shorthand, structure, info);

            pos = PAY_POS;

            if (result)
            {
                for (UINT32 i = 0; i < info.Count(); i++)
                {
                    if (i > 0)
                    {
                        ParameterUtils::Write((BYTE)';', response, MAX_SIZE, &pos, 1, m_dataAPI);
                    }
                    ParameterUtils::WriteString(info[i].Name, response, MAX_SIZE, &pos, false, m_dataAPI);
                    if (PARAMETER_STRLEN(info[i].Value) > 0)
                    {
                        ParameterUtils::Write((BYTE)'=', response, MAX_SIZE, &pos, 1, m_dataAPI);
                        ParameterUtils::WriteString(info[i].Value, response, MAX_SIZE, &pos, false, m_dataAPI);
                    }
                    ParameterUtils::Write((BYTE)',', response, MAX_SIZE, &pos, 1, m_dataAPI);
                    ParameterUtils::WriteString(info[i].Type, response, MAX_SIZE, &pos, false, m_dataAPI);
                    if (info[i].IsArray || info[i].IsOptional)
                    {
                        ParameterUtils::Write((BYTE)',', response, MAX_SIZE, &pos, 1, m_dataAPI);
                        ParameterUtils::DoubleToString((double)info[i].Elements, (CHAR*)&response[pos], MAX_SIZE - pos, 0);
                        pos = (UINT32)(pos + PARAMETER_STRLEN((CHAR*)&response[pos]));
                    }
                }
            }

            ParameterUtils::Write((BYTE)'\0', response, MAX_SIZE, &pos, 1, m_dataAPI);

            // Write the length
            UINT32 len_pos = LEN_POS;
            UINT16 rspLen = (UINT16)pos;
            ParameterUtils::Write(rspLen, response, MAX_SIZE, &len_pos, 1, m_dataAPI);

            m_com->Write(response, rspLen);

            constexpr INT32 MAX_PRINT = 128;
            CHAR printPayload[MAX_PRINT];
            SIZE_T len = PARAMETER_STRLEN((CHAR*)&response[PAY_POS]);
            if (len >= MAX_PRINT)
            {
                len = MAX_PRINT - 1;
            }
            PARAMETER_MEMCPY(printPayload, len, &response[PAY_POS], len);
            printPayload[len] = '\0';

            PARAMETER_LOG_DEBUG(m_dataAPI, "EzTune queued command: Info(%d), ID: %u, Payload: %s\n",
                response[CMD_POS], id, printPayload);

            PARAMETER_DELETE[] response;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ProcessDataGetValues
///
/// @brief Process Data Get Values
///
/// Data Get Values command Request: "<Command ID><32 bit Request ID><pipe separated mode branch>[:<structure/variable path>]\0"
/// Only a single structure or variable may be requested at a time.
/// Example struct1 request: "<Command ID><32 bit Request ID>Default|Sensor.2|Usecase.Snapshot|Feature1.ISPHDR:cc14_ipe_v2.chromatix_cc14_core.mod_cc14_trigger_data\0"
/// Example struct2 request: "<Command ID><32 bit Request ID>Default|Sensor.2|Usecase.Snapshot|Feature1.ISPHDR:cc14_ipe_v2.chromatix_cc14_reserve.ai_mod_blend_lpf_tab\0"
/// Example array request:   "<Command ID><32 bit Request ID>Default|Sensor.2|Usecase.Snapshot|Feature1.ISPHDR:cc14_ipe_v2.chromatix_cc14_reserve.ai_mod_blend_lpf_tab.ai_mod_blend_lpf\0"
/// Example element request: "<Command ID><32 bit Request ID>Default|Sensor.2|Usecase.Snapshot|Feature1.ISPHDR:cc14_ipe_v2.chromatix_cc14_reserve.ai_mod_blend_lpf_tab.ai_mod_blend_lpf[2]\0"
///
/// Data Get Values Response:"<Command ID><pipe separated mode branch>:<structure/valiable path>:<semi-colon separated list of "variable name=value">\0"
/// If a simple type variable or array is requested, the name will be removed from the path and appear only in variable name=value
/// If array is requested, array values will be comma separated: "name=value,value,value,value\0"
/// If a structure is requested, all simple types will be returned, separated by a semi-colon: "name1=value1;name2=value2\0",
/// Only simple variable types will be returned and child structures will be skipped
/// Example struct1 response: "<Command ID><32 bit Request ID>Default|Sensor.2|Usecase.Snapshot|Feature1.ISPHDR:cc14_ipe_v2.chromatix_cc14_core.mod_cc14_trigger_data:start=0;end=6\0"
/// Example struct2 response: "<Command ID><32 bit Request ID>Default|Sensor.2|Usecase.Snapshot|Feature1.ISPHDR:cc14_ipe_v2.chromatix_cc14_reserve.ai_mod_blend_lpf_tab:ai_mod_blend_lpf=13,17,22,24\0"
/// Example array response:   "<Command ID><32 bit Request ID>Default|Sensor.2|Usecase.Snapshot|Feature1.ISPHDR:cc14_ipe_v2.chromatix_cc14_reserve.ai_mod_blend_lpf_tab:ai_mod_blend_lpf=13,17,22,24\0"
/// Example element response: "<Command ID><32 bit Request ID>Default|Sensor.2|Usecase.Snapshot|Feature1.ISPHDR:cc14_ipe_v2.chromatix_cc14_reserve.ai_mod_blend_lpf_tab:ai_mod_blend_lpf[2]=22\0"
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterEzTuneWorker::ProcessDataGetValues(
    BYTE*  request,
    UINT32 length)
{
    const UINT32 MAX_SIZE = 65535;
    const UINT32 SUB_SIZE = 512;

    if (length > PAY_POS)
    {
        BYTE* response = PARAMETER_NEW BYTE[MAX_SIZE];

        if (response != nullptr)
        {
            CHAR  mode[SUB_SIZE];
            CHAR  path[SUB_SIZE];
            const CHAR* variable = nullptr;
            PList<ParameterParserInfo> info;
            UINT32 id   = 0;
            UINT32 pos = ID_POS;

            // Get ID for the log message
            ParameterUtils::Read(&id, request, length, &pos, 1, m_dataAPI);

            // Copy Tag, Command, Length, and Request ID from the request, Length will be overwritten
            PARAMETER_MEMCPY(response, MAX_SIZE, request, PAY_POS);
            response[PAY_POS] = 0;

            pos = PAY_POS;
            pos = ParameterUtils::GetSubString((CHAR*)&request[pos], ':', mode, SUB_SIZE) + pos;
            pos++;
            if (pos < length)
            {
                CHAR  shorthand[SUB_SIZE];
                pos = ParameterUtils::GetSubString((CHAR*)&request[pos], ':', path, SUB_SIZE) + pos;
                pos++;
                if (pos < length)
                {
                    variable = (CHAR*)&request[pos];
                }
                pos = PAY_POS;

                m_dataAPI->GetModeShorthand(mode, shorthand, SUB_SIZE);

                response[pos] = 0;
                m_dataAPI->GetValue(shorthand, path, variable, (CHAR*)&response[pos], MAX_SIZE - pos);
            }

            // Write the length
            UINT32 len_pos = LEN_POS;
            UINT16 rspLen = (UINT16)(PAY_POS + PARAMETER_STRLEN((CHAR*)&response[PAY_POS]) + 1);
            ParameterUtils::Write(rspLen, response, MAX_SIZE, &len_pos, 1, m_dataAPI);

            m_com->Write(response, rspLen);

            constexpr INT32 MAX_PRINT = 128;
            CHAR printPayload[MAX_PRINT];
            SIZE_T len = PARAMETER_STRLEN((CHAR*)&response[PAY_POS]);
            if (len >= MAX_PRINT)
            {
                len = MAX_PRINT - 1;
            }
            PARAMETER_MEMCPY(printPayload, len, &response[PAY_POS], len);
            printPayload[len] = '\0';

            PARAMETER_LOG_DEBUG(m_dataAPI, "EzTune queued command: GetValues(%d), ID: %u, Payload: %s\n",
                response[CMD_POS], id, printPayload);

            PARAMETER_DELETE[] response;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ProcessDataSetValues
///
/// @brief Process Data Get Values
///
/// Client Request to set values for the specified colon separated mode:path: semi-colon separated variable=value list
/// If the variable path specified is a structure or array containing simple types, values specified in list will be set
/// You may set a single value, or all simple type array elements, separated by a comma
/// Array sizes cannot be modified.  If a partial set of array values are sent, the missing values will not be updated
/// Example struct1 request: "<Command ID><32 bit Request ID>Default|Sensor.2|Usecase.Snapshot|Feature1.ISPHDR:cc14_ipe_v2.chromatix_cc14_core:mod_cc14_trigger_data=0\0"
/// Example array request:   "<Command ID><32 bit Request ID>Default|Sensor.2|Usecase.Snapshot|Feature1.ISPHDR:cc14_ipe_v2.chromatix_cc14_reserve:ai_mod_blend_lpf_tab:q_factor=2;ai_mod_blend_lpf=10,12,20,21\0"
/// Example element request: "<Command ID><32 bit Request ID>Default|Sensor.2|Usecase.Snapshot|Feature1.ISPHDR:cc14_ipe_v2.chromatix_cc14_reserve.ai_mod_blend_lpf_tab:ai_mod_blend_lpf[2]=20\0"
/// Server Set Values Response payload is will contain the variable=value list from the request with the current values, and can be used to validate the request
/// If a variable is not found, it will not be included in the response
///
/// Data Set Values Response:"<Command ID><32 bit Request ID><semi-colon separated list of "variable name=value">\0"
/// Example struct1 response: "<Command ID><32 bit Request ID>start=0;end=6\0"
/// Example struct2 response: "<Command ID><32 bit Request ID>ai_mod_blend_lpf=13,17,22,24\0"
/// Example array response:   "<Command ID><32 bit Request ID>ai_mod_blend_lpf=13,17,22,24\0"
/// Example element response: "<Command ID><32 bit Request ID>ai_mod_blend_lpf[2]=22\0"
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterEzTuneWorker::ProcessDataSetValues(
    BYTE*  request,
    UINT32 length)
{
    const UINT32 MAX_SIZE = 65535;
    const UINT32 SUB_SIZE = 512;

    if (length > PAY_POS)
    {
        BYTE* response = PARAMETER_NEW BYTE[MAX_SIZE];

        if (response != nullptr)
        {
            CHAR  mode[SUB_SIZE];
            PList<ParameterParserInfo> info;
            UINT32 id  = 0;
            UINT32 pos = ID_POS;

            // Get ID for the log message
            ParameterUtils::Read(&id, request, length, &pos, 1, m_dataAPI);

            // Copy Tag, Command, Length, and Request ID from the request, Length will be overwritten
            PARAMETER_MEMCPY(response, MAX_SIZE, request, PAY_POS);
            response[PAY_POS] = 0;


            bool result = false;
            pos = PAY_POS;
            pos = ParameterUtils::GetSubString((CHAR*)&request[pos], ':', mode, SUB_SIZE) + pos;
            pos++;
            if (pos < length)
            {
                CHAR  path[SUB_SIZE];
                pos = ParameterUtils::GetSubString((CHAR*)&request[pos], ':', path, SUB_SIZE) + pos;
                pos++;
                if (pos < length)
                {
                    CHAR  pair[SUB_SIZE];
                    CHAR  variable[SUB_SIZE];
                    CHAR  shorthand[SUB_SIZE];
                    INT32 count = 0;

                    m_dataAPI->GetModeShorthand(mode, shorthand, SUB_SIZE);

                    do
                    {
                        pos = ParameterUtils::GetSubString((CHAR*)&request[pos], ';', pair, SUB_SIZE) + pos;
                        pos++;

                        INT32 valPos = ParameterUtils::GetSubString(pair, '=', variable, SUB_SIZE);

                        if (valPos + 1 < (INT32)PARAMETER_STRLEN(pair))
                        {
                            result = m_dataAPI->SetValue(shorthand, path, variable, &pair[valPos + 1]);
                            if (result)
                            {
                                CHAR value[MAX_SIZE] = "";
                                if (m_dataAPI->GetValue(shorthand, path, variable, value, MAX_SIZE))
                                {
                                    if (count > 0)
                                    {
                                        PARAMETER_STRCAT((char*)&response[PAY_POS], MAX_SIZE - PAY_POS, ";");
                                    }
                                    PARAMETER_STRCAT((char*)&response[PAY_POS], MAX_SIZE - PAY_POS, value);
                                    count++;
                                }
                            }
                        }
                    } while (pos < length);
                }
            }

           // Write the length
           UINT32 len_pos = LEN_POS;
           UINT16 rspLen = (UINT16)(PAY_POS + PARAMETER_STRLEN((CHAR*)&response[PAY_POS]) + 1);
           ParameterUtils::Write(rspLen, response, MAX_SIZE, &len_pos, 1, m_dataAPI);

            m_com->Write(response, rspLen);

            constexpr INT32 MAX_PRINT = 128;
            CHAR printPayload[MAX_PRINT];
            SIZE_T len = PARAMETER_STRLEN((CHAR*)&response[PAY_POS]);
            if (len >= MAX_PRINT)
            {
                len = MAX_PRINT - 1;
            }
            PARAMETER_MEMCPY(printPayload, len, &response[PAY_POS], len);
            printPayload[len] = '\0';

            PARAMETER_LOG_DEBUG(m_dataAPI, "EzTune queued command: SetValues(%d), ID: %u, Payload: %s\n",
                response[CMD_POS], id, printPayload);

            PARAMETER_DELETE[] response;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ProcessCustom
///
/// @brief Process Custom Commands
///
/// Custom command Request:  "<Command ID><32 bit Request ID><16 bit payload length><payload>"
/// Custom command Response: "<Command ID><32 bit Request ID><16 bit payload length><16 bit response><payload >"
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterEzTuneWorker::ProcessCustom(
    BYTE* request,
    UINT32 length)
{
    const UINT32 MAX_SIZE    = 65535;
    const UINT32 RESULT_POS  = PAY_POS;
    const UINT32 RESULT_SIZE = 2U;
    const UINT32 RSP_PKT_POS = RESULT_POS + RESULT_SIZE;
    const UINT32 MAX_RSP     = MAX_SIZE - RSP_PKT_POS;
    if (length > PAY_POS && length <= 0xFFFF)
    {
        BYTE* response = PARAMETER_NEW BYTE[MAX_SIZE];

        if (response != nullptr)
        {
            UINT32 pos = ID_POS;
            UINT32 id = 0;

            ParameterUtils::Read(&id, request, length, &pos, 1, m_dataAPI);

            // Copy Tag, Command, Length, and Request ID from the request, Length will be overwritten
            PARAMETER_MEMCPY(response, MAX_SIZE, request, PAY_POS);

            UINT16 result  = 0;
            UINT16 rspSize = 0;
            if (m_callback != nullptr)
            {
                result = m_callback->CustomPacketHandler(&request[PAY_POS], (UINT16)length - PAY_POS, MAX_RSP, &response[RSP_PKT_POS], rspSize);
            }
            else
            {
                PARAMETER_LOG_ERROR(m_dataAPI, "EzTune queued Custom command received with no handler installed, sending result = 0\n");
            }

            // Write the length
            UINT32 len_pos = LEN_POS;
            UINT16 rspLen = RSP_PKT_POS + rspSize;
            ParameterUtils::Write(rspLen, response, MAX_SIZE, &len_pos, 1U, m_dataAPI);

            // Write the result
            UINT32 result_pos = RESULT_POS;
            ParameterUtils::Write(result, response, MAX_SIZE, &result_pos, 1U, m_dataAPI);

            m_com->Write(response, rspLen);

            constexpr INT32 MAX_PRINT = 128;
            CHAR printPayload[MAX_PRINT];
            PARAMETER_INITIALIZE_PTR(printPayload, MAX_PRINT);

            if (IsPrintable(&response[RSP_PKT_POS], rspSize))
            {
                UINT32 max = PARAMETER_MIN(rspSize, MAX_PRINT - 1);
                PARAMETER_MEMCPY(printPayload, MAX_PRINT, &response[RSP_PKT_POS], max);
            }
            else
            {
                UINT32 rspPos = 0;
                for (UINT32 printPos = 0; rspPos < rspSize && printPos < MAX_PRINT - 2; printPos += 2)
                {
                    PARAMETER_SPRINTF(&printPayload[printPos], MAX_PRINT - printPos, "%02X", response[RSP_PKT_POS + rspPos]);
                    rspPos++;
                }
            }

            PARAMETER_LOG_DEBUG(m_dataAPI, "EzTune queued command: Custom(%d), ID: %u, Payload: %s...\n",
                response[CMD_POS], id, printPayload);

            PARAMETER_DELETE[] response;
        }
    }
}

bool ParameterEzTuneWorker::IsPrintable(BYTE* bt, UINT32 len)
{
    bool result = false;
    if (bt != nullptr && len > 0)
    {
        result = true;
        for (UINT32 i = 0; result && i < len; i++)
        {
            result = IsPrintable(bt[i]);
        }
    }

    return result;
}
