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

#include "parametersetmanager.h"
#include "parameterutils.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// class ModuleLink
///
/// @brief Contains module link code.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ModuleLink::ModuleLink(ParameterModule* const module)
{
    Module = module;
    Next   = nullptr;
}

ModuleLink::~ModuleLink()
{
    PARAMETER_DELETE Module;
    PARAMETER_DELETE Next;
    Module = nullptr;
    Next   = nullptr;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// class DefaultModule
///
/// @brief Contains default module code.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
DefaultModule::DefaultModule(ParameterModule* const tmpl)
{
    Template    = tmpl;
    FirstModule = nullptr;
    LastModule  = nullptr;
}

DefaultModule::~DefaultModule()
{
    PARAMETER_DELETE FirstModule;
    PARAMETER_DELETE Template;
}

void DefaultModule::AddModule(ParameterModule* const module)
{
    if (module != nullptr)
    {
        ModuleLink* const link = PARAMETER_NEW ModuleLink(module);
        if (FirstModule == nullptr)
        {
            FirstModule = link;
        }
        else
        {
            LastModule->Next = link;
        }

        LastModule = link;
    }
}

void DefaultModule::RemoveModule(const ParameterModule* const replaced)
{
    ModuleLink* prev = nullptr;
    ModuleLink* link = FirstModule;
    while (link != nullptr && link->Module != replaced)
    {
        prev = link;
        link = link->Next;
    }
    if (link != nullptr)
    {
        if (link == FirstModule)
        {
            FirstModule = link->Next;
        }
        else if (prev != nullptr)
        {
            prev->Next = link->Next;
        }
        link->Module = nullptr;
        link->Next   = nullptr;
        PARAMETER_DELETE link;
    }
}

ParameterModule* DefaultModule::Find(UINT32 const id)
{
    ParameterModule* result = nullptr;
    ModuleLink* current = FirstModule;
    while (current != nullptr && current->Module->Data != nullptr && current->Module->Data->ID != id)
    {
        current = current->Next;
    }
    if (current != nullptr)
    {
        result = current->Module;
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// class ParameterCTValue
///
/// @brief Contains parameter control/trigger value.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ParameterCTValue::ParameterCTValue()
{
    EnumVal   = 0;
    Value.Int = 0;
    IsInt     = true;
    Valid     = false;
}

ParameterCTValue::ParameterCTValue(INT32 const enumVal, INT32 const value)
{
    EnumVal = enumVal;
    SetValue(value);
}

ParameterCTValue::ParameterCTValue(INT32 const enumVal, UINT32 const value)
{
    EnumVal = enumVal;
    SetValue(value);
}

ParameterCTValue::ParameterCTValue(INT32 const enumVal, INT64 const value)
{
    EnumVal = enumVal;
    SetValue(value);
}

ParameterCTValue::ParameterCTValue(INT32 const enumVal, float const value)
{
    EnumVal = enumVal;
    SetValue(value);
}

ParameterCTValue::ParameterCTValue(INT32 const enumVal, double const value)
{
    EnumVal = enumVal;
    SetValue(value);
}

bool ParameterCTValue::FromString(const CHAR* const input)
{
    Valid = false;

    if (input != nullptr)
    {
        constexpr SIZE_T IDX_SIZE = 10;
        CHAR   evStr[IDX_SIZE];
        const SIZE_T len = PARAMETER_STRLEN(input);
        INT32 pos = ParameterUtils::GetSubString(input, '=', evStr, IDX_SIZE);
        if (pos > 0 && pos + 1 < (INT32)len)
        {
            pos++;
            EnumVal = (INT32)ParameterUtils::StringToDouble(evStr);
            IsInt   = ParameterUtils::GetStringPos(&input[pos], '.') < 0;
            if (IsInt)
            {
                Value.Int = (INT64)ParameterUtils::StringToDouble(&input[pos]);
            }
            else
            {
                Value.Float = ParameterUtils::StringToDouble(&input[pos]);
            }
            Valid = true;
        }
    }

    return Valid;
}

bool ParameterCTValue::ToString(CHAR* const str, UINT32 const size, bool const addEnumVal)
{
    if (Valid)
    {
        if (addEnumVal)
        {
            if (IsInt)
            {
                PARAMETER_SPRINTF(str, size, "%u=%lld", EnumVal, Value.Int);
            }
            else
            {
                PARAMETER_SPRINTF(str, size, "%u=%f", EnumVal, Value.Float);
            }
        }
        else
        {
            if (IsInt)
            {
                PARAMETER_SPRINTF(str, size, "%lld", Value.Int);
            }
            else
            {
                PARAMETER_SPRINTF(str, size, "%f", Value.Float);
            }
        }
    }

    return Valid;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// class ModeEntry
///
/// @brief Contains an mode tree data.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Constructor
///
/// @brief  Constructs the ModeEntry class
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ModeEntry::ModeEntry(
    bool const loaded,
    bool const preserve)
{
    Id          = 0;
    Loaded      = loaded;
    Preserve    = preserve;
    Link        = nullptr;
    Parent      = nullptr;
    FirstChild  = nullptr;
    LastChild   = nullptr;
    Next        = nullptr;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Destructor
///
/// @brief  Destructs the ModeEntry class
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ModeEntry::~ModeEntry()
{
    PARAMETER_DELETE FirstChild;
    FirstChild = nullptr;
    LastChild  = nullptr;

    PARAMETER_DELETE Next;
    Next = nullptr;

    ModuleMap.clear();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Initialize
///
/// @brief  Initializes the ModeEntry class
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ModeEntry::Initialize(
    UINT32 const     id,
    const ModeUnion&  mode,
    ModeEntry** const link)
{
    Id          = id;
    Mode        = mode;
    Link        = link;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Initialize
///
/// @brief  Initializes the ModeEntry class
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ModeEntry::Initialize(
    UINT32 const      id,
    UINT16 const      mode,
    UINT16 const      subMode,
    ModeEntry** const link)
{
    Id                = id;
    Mode.Mode.Mode    = mode;
    Mode.Mode.SubMode = subMode;
    Link              = link;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Add
///
/// @brief  Adds a child ModeEntry
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ModeEntry::AddMode(
    ModeEntry* const child)
{
    if (child != nullptr)
    {
        child->Parent = this;

        // Child modes define the mode tree structure
        if (FirstChild == nullptr)
        {
            FirstChild = child;
        }
        else
        {
            LastChild->Next = child;
        }
        LastChild  = child;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Add
///
/// @brief  Adds a Module
///
/// @return Returns the ModeEntry object or nullptr, if not found
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ModeEntry::AddModule(
   ParameterModule* const module)
{
    if (module != nullptr)
    {
        if (ModuleMap.find(std::string(module->Name)) == ModuleMap.end())
        {
            ModuleMap[std::string(module->Name)] = ModeEntry::ModuleReference(module);
        }
    }

    return module != nullptr;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ReplaceModule
///
/// @brief  Replaces existing or adds a new Module.
///
/// @return Replaced module, nullptr if this is a new module
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ParameterModule* ModeEntry::ReplaceModule(
    ParameterModule* const newModule)
{
    ParameterModule* replaced = nullptr;
    if (newModule != nullptr)
    {
        if (ModuleMap.find(std::string(newModule->Name)) != ModuleMap.end())
        {
            replaced = ModuleMap[std::string(newModule->Name)].Module;
        }
        ModuleMap[std::string(newModule->Name)] = ModeEntry::ModuleReference(newModule);
    }

    return replaced;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// FindMode
///
/// @brief  Finds a ModeEntry, in children, from a mode, subMode
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ModeEntry* ModeEntry::FindMode(
   const TuningMode* const mode)
{
    ModeEntry* result = nullptr;

    if (Link != nullptr)
    {
        result = (*Link)->FindMode(mode);
    }
    else
    {
        ModeEntry* child = FirstChild;
        while (result == nullptr && child != nullptr)
        {
            if ((UINT16)mode->mode  == child->Mode.Mode.Mode &&
                mode->subMode.value == child->Mode.Mode.SubMode)
            {
                result = child;
            }
            else
            {
                child = child->Next;
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// FindMode
///
/// @brief  Finds a ModeEntry, in children, from a mode/subMode array
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ModeEntry* ModeEntry::FindMode(
    const TuningMode* const modes,
    UINT32 const count,
    UINT32 const found)
{
    ModeEntry* result = nullptr;

    if (Link != nullptr)
    {
        result = (*Link)->FindMode(modes, count, found);
    }
    else
    {
        for (UINT32 index = 0; result == nullptr && index < count; index++)
        {
            result = FindMode(&modes[index]);

            if (result != nullptr && found + 1 < count)
            {
                result = result->FindMode(modes, count, found + 1);
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// FindModule
///
/// @brief  Finds a Module from a module name id
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ParameterModule* ModeEntry::FindModule(
    const std::string* const moduleName)
{
    ParameterModule* result = nullptr;

    if (Link != nullptr)
    {
        result = (*Link)->FindModule(moduleName);
    }
    else
    {
        if (ModuleMap.find(*moduleName) != ModuleMap.end())
        {
            result = ModuleMap[*moduleName].Module;
        }
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetModePath
///
/// @brief  Build a path to this mode.
///         When first called, modePath must be empty because this is a recursive function
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ModeEntry::GetModePath(
    CHAR*        modePath,
    UINT32 const pathSize)
{
    if (Parent != nullptr)
    {
        Parent->GetModePath(modePath, pathSize);
    }

    SIZE_T len = PARAMETER_STRLEN(modePath);
    if (len > 0)
    {
        PARAMETER_STRCAT(modePath, pathSize, "|");
        len++;
    }
    if (len < pathSize)
    {
        PARAMETER_SPRINTF(&modePath[len], pathSize - len, "%u.%u", Mode.Mode.Mode, Mode.Mode.SubMode);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// AddModuleReference
///
/// @brief  Add a reference to the module
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ModeEntry::AddReference(
    const ParameterModule* const module)
{
    if (module != nullptr)
    {
        std::unordered_map<std::string, ModuleReference>::iterator const iter = ModuleMap.find(module->Name);
        if (iter != ModuleMap.end())
        {
            iter->second.RefCount++;
        }
        else
        {
            ParameterSetManager* mgr = nullptr;
            if (module->Data != nullptr)
            {
                mgr = module->Data->SetManager;
            }
            PARAMETER_LOG_ERROR(mgr, "Attempt to add reference to a non-existant module.  Mode: %d, SubMode: %d, Module %s\n",
                Mode.Mode.Mode, Mode.Mode.SubMode, module->Name);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ReleaseModuleReference
///
/// @brief  Releases a reference to the module
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ModeEntry::ReleaseReference(
    const ParameterModule* const module)
{
    if (module != nullptr)
    {
        std::unordered_map<std::string, ModuleReference>::iterator const iter = ModuleMap.find(module->Name);
        if (iter != ModuleMap.end())
        {
            if (iter->second.RefCount > 0)
            {
                iter->second.RefCount--;
            }
            else
            {
                ParameterSetManager* mgr = nullptr;
                if (module->Data != nullptr)
                {
                    mgr = module->Data->SetManager;
                }
                PARAMETER_LOG_ERROR(mgr, "Attempt to release unreferenced module.  Mode: %d, SubMode: %d, Module %s\n",
                    Mode.Mode.Mode, Mode.Mode.SubMode, module->Name);
            }
        }
        else
        {
            ParameterSetManager* mgr = nullptr;
            if (module->Data != nullptr)
            {
                mgr = module->Data->SetManager;
            }
            PARAMETER_LOG_ERROR(mgr, "Attempt to release a non-existant module.  Mode: %d, SubMode: %d, Module %s\n",
                Mode.Mode.Mode, Mode.Mode.SubMode, module->Name);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ReleaseModuleReferences
///
/// @brief  Sets the reference count for all modules to zero, called when unloading a mode
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ModeEntry::ReleaseReferences()
{
    std::unordered_map<std::string, ModuleReference>::iterator iter = ModuleMap.begin();
    while (iter != ModuleMap.end())
    {
        // Release references for modules belonging to this mode (don't release linked modules)
        if (iter->second.Module != nullptr && iter->second.Module->Data != nullptr &&
            iter->second.Module->Data->Mode == this)
        {
            if (iter->second.RefCount > 0)
            {
                ParameterSetManager* mgr = iter->second.Module->Data->SetManager;
                PARAMETER_LOG_ERROR(mgr, "ReleaseReferences called with unreleased module: %s, RefCnt: %d\n",
                    iter->second.Module->Name, iter->second.RefCount);
            }
            iter->second.RefCount = 0;
        }
        iter++;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetReferenceCount
///
/// @brief  Returns the reference count of the specified module.  If module is nullptr,
///         returns the total reference count of all modules combined
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT32 ModeEntry::GetReferenceCount(
    const ParameterModule* const module)
{
    UINT32 result = 0;

    std::unordered_map<std::string, ModeEntry::ModuleReference>::iterator iter;
    if (module != nullptr)
    {
        iter = ModuleMap.find(module->Name);
    }
    else
    {
        iter = ModuleMap.begin();
    }

    while (iter != ModuleMap.end())
    {
        if (iter->second.Module != nullptr && iter->second.Module->Data != nullptr &&
            iter->second.Module->Data->Mode == this)
        {
            result += iter->second.RefCount;
        }
        iter++;
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IsActive
///
/// @brief  Returns true if one or more local modules is referenced
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ModeEntry::IsActive()
{
    bool result = false;

    std::unordered_map<std::string, ModeEntry::ModuleReference>::iterator iter = ModuleMap.begin();
    while (!result && iter != ModuleMap.end())
    {
        if (iter->second.Module != nullptr && iter->second.Module->Data != nullptr &&
            iter->second.Module->Data->Mode == this)
        {
            result = iter->second.RefCount > 0;
        }
        iter++;
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// HasLocalModules
///
/// @brief  Returns true if the mode has any local modules
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ModeEntry::HasLocalModules()
{
    bool result = false;

    std::unordered_map<std::string, ModeEntry::ModuleReference>::iterator iter = ModuleMap.begin();
    while (!result && iter != ModuleMap.end())
    {
        result = iter->second.Module != nullptr && iter->second.Module->Data != nullptr &&
            iter->second.Module->Data->Mode == this;
        iter++;
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IsParent
///
/// @brief  Returns true if mode is in the parent chain of this mode
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ModeEntry::IsParent(
    ModeEntry* mode)
{
    bool result = false;

    if (mode != nullptr && Parent != nullptr)
    {
        result = Parent == mode;

        if (!result)
        {
            result = Parent->IsParent(mode);
        }
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetModeName
///
/// @brief  Returns the Mode name, dependent on ParameterTuningTypes.xsd used to generate source
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const CHAR* ModeEntry::GetModeName()
{
    const CHAR* result = nullptr;

    switch (Mode.Mode.Mode)
    {
    case (UINT16)ModeType::Default:
        result = "Default";
        break;
    case (UINT16)ModeType::Sensor:
        result = "Sensor";
        break;
    case (UINT16)ModeType::Usecase:
        result = "Usecase";
        break;
    case (UINT16)ModeType::Feature1:
        result = "Feature1";
        break;
    case (UINT16)ModeType::Feature2:
        result = "Feature2";
        break;
    case (UINT16)ModeType::Scene:
        result = "Scene";
        break;
    case (UINT16)ModeType::Effect:
        result = "Effect";
        break;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetSubModeName
///
/// @brief  Returns the Sub-Mode name, dependent on ParameterTuningTypes.xsd used to generate source
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const CHAR* ModeEntry::GetSubModeName()
{
    const CHAR* result = nullptr;

    switch (Mode.Mode.Mode)
    {
    case (UINT16)ModeType::Default:
        break;
    case (UINT16)ModeType::Sensor:
        break;
    case (UINT16)ModeType::Usecase:
        switch (Mode.Mode.SubMode)
        {
        case (UINT16)ModeUsecaseSubModeType::Preview:
            result = "Preview";
            break;
        case (UINT16)ModeUsecaseSubModeType::Snapshot:
            result = "Snapshot";
            break;
        case (UINT16)ModeUsecaseSubModeType::Video:
            result = "Video";
            break;
        case (UINT16)ModeUsecaseSubModeType::ZSL:
            result = "ZSL";
            break;
        case (UINT16)ModeUsecaseSubModeType::Liveshot:
            result = "Liveshot";
            break;
        case (UINT16)ModeUsecaseSubModeType::SnapshotReprocess:
            result = "SnapshotReprocess";
            break;
        case (UINT16)ModeUsecaseSubModeType::IDEALRAW:
            result = "IDEALRAW";
            break;
        case (UINT16)ModeUsecaseSubModeType::IdealRawReprocess:
            result = "IdealRawReprocess";
            break;
        }
        break;
    case (UINT16)ModeType::Feature1:
        switch (Mode.Mode.SubMode)
        {
        case (UINT16)ModeFeature1SubModeType::None:
            result = "None";
            break;
        case (UINT16)ModeFeature1SubModeType::ISPHDR:
            result = "ISPHDR";
            break;
        case (UINT16)ModeFeature1SubModeType::ADRC:
            result = "ADRC";
            break;
        case (UINT16)ModeFeature1SubModeType::inSensorHDR3Exp:
            result = "inSensorHDR3Exp";
            break;
        case (UINT16)ModeFeature1SubModeType::MFHDRPreFusion:
            result = "MFHDRPreFusion";
            break;
        case (UINT16)ModeFeature1SubModeType::MFHDRFusion:
            result = "MFHDRFusion";
            break;
        case (UINT16)ModeFeature1SubModeType::MFHDRPostFusion:
            result = "MFHDRPostFusion";
            break;
        }
        break;
    case (UINT16)ModeType::Feature2:
        switch (Mode.Mode.SubMode)
        {
        case (UINT16)ModeFeature2SubModeType::None:
            result = "None";
            break;
        case (UINT16)ModeFeature2SubModeType::OISCapture:
            result = "OISCapture";
            break;
        case (UINT16)ModeFeature2SubModeType::HLG:
            result = "HLG";
            break;
        case (UINT16)ModeFeature2SubModeType::HDR10:
            result = "HDR10";
            break;
        case (UINT16)ModeFeature2SubModeType::MFNRBlend:
            result = "MFNRBlend";
            break;
        case (UINT16)ModeFeature2SubModeType::MFNRPostFilter:
            result = "MFNRPostFilter";
            break;
        case (UINT16)ModeFeature2SubModeType::AIDenoiserMFNRBlend:
            result = "AIDenoiserMFNRBlend";
            break;
        case (UINT16)ModeFeature2SubModeType::AIDenoiserMFNRPostFilter:
            result = "AIDenoiserMFNRPostFilter";
            break;
        case (UINT16)ModeFeature2SubModeType::QLLMFNRBlend:
            result = "QLLMFNRBlend";
            break;
        case (UINT16)ModeFeature2SubModeType::QLLMFNRPostFilter:
            result = "QLLMFNRPostFilter";
            break;
        case (UINT16)ModeFeature2SubModeType::QLLPostFusion:
            result = "QLLPostFusion";
            break;
        }
        break;
    case (UINT16)ModeType::Scene:
        switch (Mode.Mode.SubMode)
        {
        case (UINT16)ModeSceneSubModeType::None:
            result = "None";
            break;
        case (UINT16)ModeSceneSubModeType::Landscape:
            result = "Landscape";
            break;
        case (UINT16)ModeSceneSubModeType::Snow:
            result = "Snow";
            break;
        case (UINT16)ModeSceneSubModeType::Beach:
            result = "Beach";
            break;
        case (UINT16)ModeSceneSubModeType::Sunset:
            result = "Sunset";
            break;
        case (UINT16)ModeSceneSubModeType::Night:
            result = "Night";
            break;
        case (UINT16)ModeSceneSubModeType::Portrait:
            result = "Portrait";
            break;
        case (UINT16)ModeSceneSubModeType::BackLight:
            result = "BackLight";
            break;
        case (UINT16)ModeSceneSubModeType::Sports:
            result = "Sports";
            break;
        case (UINT16)ModeSceneSubModeType::AntiShake:
            result = "AntiShake";
            break;
        case (UINT16)ModeSceneSubModeType::Flowers:
            result = "Flowers";
            break;
        case (UINT16)ModeSceneSubModeType::CandleLight:
            result = "CandleLight";
            break;
        case (UINT16)ModeSceneSubModeType::Fireworks:
            result = "Fireworks";
            break;
        case (UINT16)ModeSceneSubModeType::Party:
            result = "Party";
            break;
        case (UINT16)ModeSceneSubModeType::NightPortrait:
            result = "NightPortrait";
            break;
        case (UINT16)ModeSceneSubModeType::Theater:
            result = "Theater";
            break;
        case (UINT16)ModeSceneSubModeType::Action:
            result = "Action";
            break;
        case (UINT16)ModeSceneSubModeType::AR:
            result = "AR";
            break;
        case (UINT16)ModeSceneSubModeType::FacePriority:
            result = "FacePriority";
            break;
        case (UINT16)ModeSceneSubModeType::Barcode:
            result = "Barcode";
            break;
        case (UINT16)ModeSceneSubModeType::BestShot:
            result = "BestShot";
            break;
        }
        break;
    case (UINT16)ModeType::Effect:
        switch (Mode.Mode.SubMode)
        {
        case (UINT16)ModeEffectSubModeType::None:
            result = "None";
            break;
        case (UINT16)ModeEffectSubModeType::Mono:
            result = "Mono";
            break;
        case (UINT16)ModeEffectSubModeType::Sepia:
            result = "Sepia";
            break;
        case (UINT16)ModeEffectSubModeType::Negative:
            result = "Negative";
            break;
        case (UINT16)ModeEffectSubModeType::Solarize:
            result = "Solarize";
            break;
        case (UINT16)ModeEffectSubModeType::Posterize:
            result = "Posterize";
            break;
        case (UINT16)ModeEffectSubModeType::Aqua:
            result = "Aqua";
            break;
        case (UINT16)ModeEffectSubModeType::Emboss:
            result = "Emboss";
            break;
        case (UINT16)ModeEffectSubModeType::Sketch:
            result = "Sketch";
            break;
        case (UINT16)ModeEffectSubModeType::Neon:
            result = "Neon";
            break;
        case (UINT16)ModeEffectSubModeType::Blackboard:
            result = "Blackboard";
            break;
        case (UINT16)ModeEffectSubModeType::Whiteboard:
            result = "Whiteboard";
            break;
        }
        break;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ParameterSetManager
///
/// @brief Manages the parameter set
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// LoadBinaryParameters
///
/// @brief Loads binary parameters from a buffer
///
/// @param buffer   Binary parameter buffer
/// @param length   Length of the binary parameter buffer
///
/// @return Node
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::LoadBinaryParameters(
    BYTE* const buffer,
    SIZE_T const length)
{
    UINT32 pos       = 0;
    UINT32 offset    = 0;
    BYTE   alignment = 1;

    PARAMETER_LOG_DEBUG(this, "Load Binary Parameters from buffer\n");

    Valid = false;

    if (SymbolTable != nullptr)
    {
        SetError("Binary file already loaded");
    }
    else if (length <= UINT_MAX)
    {
        if (ReadHeader(buffer, (UINT32)length, &pos, &offset, &alignment))
        {
            Valid = ReadSections(buffer, (UINT32)length, nullptr, nullptr, 0, offset, this, alignment);
            if (Valid)
            {
                PARAMETER_LOG_DEBUG(this, "Load Binary Parameters completed: Tag: %s, Version: %d.%d.%d\n",
                    BinaryTag, Version.Version.Major, Version.Version.Minor, Version.Version.Patch);

#ifdef EZTUNE
                if (m_ezTune != nullptr && EzTuneAuto)
                {
                    m_ezTune->Start();
                }
#endif
            }
        }
    }
    else
    {
        PARAMETER_LOG_ERROR(this, "Buffer length (%llu) exceeds maximum %lu\n", length, UINT_MAX);
    }

    return Valid;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// LoadBinaryParameters
///
/// @brief Loads binary parameters from a file
///
/// @param buffer   Binary parameter file name
///
/// @return Node
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::LoadBinaryParameters(
    const CHAR* file)
{
    if (file != nullptr && PARAMETER_STRLEN(file) < MAX_FILE_LENGTH - 1)
    {
        ParameterBinaryFile* fp = nullptr;

        PARAMETER_LOG_DEBUG(this, "Load Binary Parameters from file: %s\n", file);

        Valid = false;

        if (SymbolTable != nullptr)
        {
            SetError("Binary file already loaded");
        }
        else
        {
            fp = PARAMETER_NEW ParameterBinaryFile(file, this);

            if (fp != nullptr && fp->IsValidCRC())
            {
                UINT32 length = fp->Length();
                BYTE*  buffer = fp->Read(0, length);

                if (buffer != nullptr)
                {
                    if (!DelayLoad)
                    {
                        Valid = LoadBinaryParameters(buffer, length);
                    }
                    else
                    {
                        UINT32 pos       = 0;
                        UINT32 offset    = 0;
                        BYTE   alignment = 1;
                        UINT32 hsLength  = HEADER_LEN + MAX_SECTIONS * SECTION_SIZE + 100;

                        if (file != m_file)
                        {
                            PARAMETER_STRCPY(m_file, MAX_FILE_LENGTH, file);
                        }

                        if (ReadHeader(buffer, (UINT32)length, &pos, &offset, &alignment))
                        {
                            // Free full buffer, and allocate just enough to read the header and section info
                            PARAMETER_DELETE[] buffer;
                            buffer = fp->Read(0, hsLength);

                            if (buffer != nullptr)
                            {
                                pos = (INT32)offset;

                                if ((Flags & FLAGS_COMPRESSED) > 0)
                                {
                                    PARAMETER_LOG_ERROR(this, "Compressed binary files ssupport removed in V5.1.0\n");
                                    Valid = false;
                                }
                                else
                                {
                                    Valid = ReadSections(buffer, hsLength, m_file, fp, (UINT32)length, pos, this, alignment);
                                    if (Valid)
                                    {
                                        PARAMETER_LOG_DEBUG(this, "Load Binary Parameters completed: Binary Version: %d.%d.%d\n",
                                            Version.Version.Major, Version.Version.Minor, Version.Version.Patch);

#ifdef EZTUNE
                                        if (m_ezTune != nullptr && EzTuneAuto)
                                        {
                                            m_ezTune->Start();
                                        }
#endif
                                    }
                                }
                            }
                            else
                            {
                                PARAMETER_LOG_ERROR(this, "Failed to read %d bytes from binary file %s\n", hsLength, file);
                            }
                        }
                    }

                    PARAMETER_DELETE[] buffer;
                }
                else
                {
                    SetError("Unable to allocate space to read binary file");
                }

                if (SymbolTable != nullptr)
                {
                    // Loading the symbol table from a file will automatically add a file reference, remove it when we are done
                    // This will also close the file for us
                    SymbolTable->FileReference(-1);
                }
                else
                {
                    PARAMETER_DELETE fp;
                }
                fp = nullptr;
            }
            else
            {
                PARAMETER_LOG_ERROR(this, "Failed to load binary file %s\n", file);
            }
        }
    }
    else if (file == nullptr)
    {
        SetError("LoadBinaryParameters called with nullptr file name");
    }
    else
    {
        PARAMETER_LOG_ERROR(this, "File name length (%lld) exceeds max file name length of %d\n",
            PARAMETER_STRLEN(file), MAX_FILE_LENGTH - 1);
    }
    return Valid;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetValue
///
/// @brief Sets the module value specified by the mode branch, and variable path
///
/// @param modePath     Pipe '|' separated Mode path.
///                     Example: "0|1.0|2.0" (represents Default|Sensor.0|Preview)
/// @param path         Dot '.' separated Path to the variable to set.
///                     Example: cc14_ipr_v2.control_variables.control_var_type[2]
/// @param variable     Variable to get, if nullptr, path is used explicitly
///                     Example: control_var_type[2]
/// @param value        Value to get
/// @param size         Size of the Value string
///
/// @return True value was found and set
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::GetValue(
    const CHAR* const modePath,
    const CHAR* const path,
    const CHAR* const variable,
    CHAR* const       value,
    UINT32 const      size)
{
    bool result = false;
    if (modePath != nullptr && path != nullptr && value != nullptr)
    {
        PList<TuningMode> modeList;

        result = GetModeBranchFromPath(modePath, modeList);

        if (result)
        {
            TuningMode* modeBranch = PARAMETER_NEW TuningMode[modeList.Count()];
            PARAMETER_INITIALIZE_PTR(modeBranch, modeList.Count());

            for (UINT32 i = 0; i < modeList.Count(); i++)
            {
                PARAMETER_MEMCPY(&modeBranch[i], sizeof(TuningMode), &modeList[i], sizeof(TuningMode));
            }
            result = GetValue(modeBranch, modeList.Count(), path, variable, value, size);

            PARAMETER_DELETE[] modeBranch;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetValue
///
/// @brief Sets the module value specified by the mode path, and variable path
///
/// @param modeBranch   Mode branch
/// @param modeCount    Mode count
/// @param path         Dot '.' separated Path to the variable to set.
///                     Example: cc14_ipr_v2.control_variables.control_var_type[2]
/// @param variable     Variable to get, if nullptr, path is used explicitly
///                     Example: control_var_type[2]
/// @param value        Output: semi-colon Name=Value list
/// @param size         Size of the Value string
///
/// @return True value was found and set
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::GetValue(
    const TuningMode* const modeBranch,
    UINT32 const            modeCount,
    const CHAR* const       path,
    const CHAR* const       variable,
    CHAR* const             value,
    UINT32 const            size)
{
    constexpr INT32 MAX_VAR_PATH = 4096;
    constexpr INT32 MAX_SIZE     = 65535;
    bool result = false;

    if (modeBranch != nullptr && modeCount > 0 && path != nullptr && value != nullptr && size > 0)
    {
        constexpr UINT32 MAX_MODULE = 65U;
        CHAR moduleName[MAX_MODULE];
        UINT32 pos = ParameterUtils::GetSubString(path, '.', moduleName, MAX_MODULE);

        value[0] = '\0';
        {
            const ParameterMutexLock rlock {ReloadMutex, true, true}; // Shared LOCK

            std::string const strName(moduleName);
            ParameterModule* const module = GetModule(moduleName, modeBranch, modeCount, nullptr);

            if (module != nullptr)
            {
                INT32 count = 0;
                CHAR varPath[MAX_VAR_PATH] = "";
                CHAR tempValue[MAX_SIZE]   = "";
                CHAR varName[MAX_VAR_PATH] = "";

                if (variable != nullptr)
                {
                    SIZE_T const varLen = PARAMETER_STRLEN(variable);
                    UINT32 varPos = 0;

                    do
                    {
                        UINT32 const len = ParameterUtils::GetSubString(&variable[varPos], ',', varName, MAX_VAR_PATH);
                        varPos += len + 1;

                        varPath[0] = '\0';
                        if (pos > 0 && pos + 2 < PARAMETER_STRLEN(path))
                        {
                            PARAMETER_STRCAT(varPath, MAX_VAR_PATH, &path[pos + 1]);
                            PARAMETER_STRCAT(varPath, MAX_VAR_PATH, ".");
                        }

                        PARAMETER_STRCAT(varPath, MAX_VAR_PATH, varName);

                        result = module->GetValue(varPath, tempValue, MAX_SIZE);
                        if (result)
                        {
                            if (count > 0)
                            {
                                PARAMETER_STRCAT(value, size, ";");
                            }
                            PARAMETER_STRCAT(value, size, varName);
                            PARAMETER_STRCAT(value, size, "=");
                            PARAMETER_STRCAT(value, size, tempValue);
                            count++;
                        }
                    } while (varPos < varLen);
                }
                else
                {
                    PList<ParameterParserInfo> info;
                    result = module->GetInfo(&path[pos + 1], info);

                    if (result)
                    {
                        for (UINT32 i = 0; result && i < info.Count(); i++)
                        {
                            if (info[i].Type != ParameterParserDataType::Module && info[i].Type != ParameterParserDataType::Structure)
                            {
                                PARAMETER_STRCPY(varPath, MAX_VAR_PATH, &path[pos + 1]);
                                if (info[i].Name != nullptr)
                                {
                                    PARAMETER_STRCAT(varPath, MAX_VAR_PATH, ".");
                                    PARAMETER_STRCAT(varPath, MAX_VAR_PATH, info[i].Name);
                                }
                                if (info[i].IsArray)
                                {
                                    if (count > 0)
                                    {
                                        PARAMETER_STRCAT(value, size, ";");
                                    }
                                    PARAMETER_STRCAT(value, size, info[i].Name);
                                    PARAMETER_STRCAT(value, size, "=");
                                    count++;

                                    INT32 const nameEnd = (INT32)PARAMETER_STRLEN(varPath);

                                    for (UINT32 element = 0; result && element < info[i].Elements; element++)
                                    {
                                        varPath[nameEnd] = '\0';
                                        PARAMETER_STRCAT(varPath, MAX_VAR_PATH, "[");
                                        ParameterUtils::DoubleToString(element, &varPath[nameEnd + 1], MAX_VAR_PATH - nameEnd + 1, 0);
                                        PARAMETER_STRCAT(varPath, MAX_VAR_PATH, "]");

                                        result = module->GetValue(varPath, tempValue, MAX_MODULE);

                                        if (element > 0)
                                        {
                                            PARAMETER_STRCAT(value, size, ",");
                                        }
                                        PARAMETER_STRCAT(value, size, tempValue);
                                    }
                                }
                                else
                                {
                                    result = module->GetValue(varPath, tempValue, MAX_MODULE);
                                    if (result)
                                    {
                                        if (count > 0)
                                        {
                                            PARAMETER_STRCAT(value, size, ";");
                                        }
                                        PARAMETER_STRCAT(value, size, info[i].Name);
                                        PARAMETER_STRCAT(value, size, "=");
                                        PARAMETER_STRCAT(value, size, tempValue);
                                        count++;
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        pos++;
                        result = module->GetValue(&path[pos], tempValue, MAX_SIZE);
                        if (result)
                        {
                            UINT32 endPos = 0;
                            SIZE_T const endLen = PARAMETER_STRLEN(&path[pos]);
                            do
                            {
                                endPos += ParameterUtils::GetSubString(&path[pos + endPos], '.', varName, MAX_VAR_PATH);
                                endPos++;
                            } while (endPos < endLen);

                            PARAMETER_STRCAT(value, size, varName);
                            PARAMETER_STRCAT(value, size, "=");
                            PARAMETER_STRCAT(value, size, tempValue);
                        }
                    }
                }
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SetValue
///
/// @brief Sets the module value specified by the mode branch, and variable path
///
/// @param modePath     Pipe '|' separated Mode path.
///                     Example: "0|1.0|2.0" (represents Default|Sensor.0|Preview)
/// @param path         Dot '.' separated Path to the variable to set.
///                     Example: cc14_ipr_v2.control_variables.control_var_type[2]
/// @param value        Value to set, will be parsed from string to the variable's type
///
/// @return True value was found and set
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::SetValue(
    const CHAR* const modePath,
    const CHAR* const path,
    const CHAR* const variable,
    const CHAR* const value)
{
    bool result = false;
    if (modePath != nullptr && path != nullptr && value != nullptr)
    {
        PList<TuningMode> modeList;

        result = GetModeBranchFromPath(modePath, modeList);

        if (result)
        {
            TuningMode* modeBranch = PARAMETER_NEW TuningMode[modeList.Count()];
            PARAMETER_INITIALIZE_PTR(modeBranch, modeList.Count());

            for (UINT32 i = 0; i < modeList.Count(); i++)
            {
                PARAMETER_MEMCPY(&modeBranch[i], sizeof(TuningMode), &modeList[i], sizeof(TuningMode));
            }
            result = SetValue(modeBranch, modeList.Count(), path, variable, value);

            PARAMETER_DELETE[] modeBranch;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SetValue
///
/// @brief Sets the module value specified by the mode path, and variable path
///
///        SetValue will force a switch to binary buffer mode if in file load mode
/// @param modeBranch   Mode branch
/// @param modeCount    Mode count
/// @param path         Dot '.' separated Path to the variable to set.
///                     Example: cc14_ipr_v2.control_variables.control_var_type[2]
/// @param value        Value to set, will be parsed from string to the variable's type
///
/// @return True value was found and set
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::SetValue(
    const TuningMode* const modeBranch,
    UINT32 const            modeCount,
    const CHAR* const       path,
    const CHAR* const       variable,
    const CHAR* const       value)
{
    constexpr INT32 MAX_VAR_PATH = 4096;
    bool result = false;

    if (modeBranch != nullptr && modeCount > 0 && path != nullptr && value != nullptr)
    {
        constexpr UINT32 MAX_MODULE = 65U;
        CHAR moduleName[MAX_MODULE];

        UINT32 const pos = ParameterUtils::GetSubString(path, '.', moduleName, MAX_MODULE);

        if (pos > 0)
        {
            const ParameterMutexLock rlock {ReloadMutex, true, true}; // Shared LOCK

            ParameterModule* const module = GetModule(moduleName, modeBranch, modeCount, nullptr);
            if (module != nullptr)
            {
                char varPath[MAX_VAR_PATH] = "";
                if (pos < PARAMETER_STRLEN(path))
                {
                    PARAMETER_STRCPY(varPath, MAX_VAR_PATH, &path[pos + 1]);
                    PARAMETER_STRCAT(varPath, MAX_VAR_PATH, ".");
                }
                PARAMETER_STRCAT(varPath, MAX_VAR_PATH, variable);

                result = module->SetValue(varPath, value);

                if (result)
                {
                    EzTuneUpdated++;
                    if (module->Data != nullptr && module->Data->Mode != nullptr && !module->Data->Mode->Preserve)
                    {
                        const ParameterMutexLock mlock{ ModuleMutex, false, IsFileMode() }; // Unique LOCK
                        module->Data->Mode->Preserve = true;
                        m_activeModes.Remove(module->Data->Mode);
                    }
                }
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetInfo
///
/// @brief Returns information about a specific parameter, or returns the list of parameters from the specified path
///
/// @param modePath     Pipe '|' separated Mode path. If nullptr, last accessed tuning mode branch will be used
///                     Example: "0|1.0|2.0" (represents Default|Sensor.0|Preview)
/// @param path         Dot '.' separated Path to the variable to query.
///                     Example: cc14_ipr_v2.control_variables.control_var_type
/// @param __info       list of info
///
/// @return True path was found
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::GetInfo(
    const CHAR* const           modePath,
    const CHAR* const           path,
    PList<ParameterParserInfo>& info)
{
    bool result = false;
    if (modePath != nullptr)
    {
        PList<TuningMode> modeList;

        result = GetModeBranchFromPath(modePath, modeList);

        if (result)
        {
            TuningMode* modeBranch = PARAMETER_NEW TuningMode[modeList.Count()];
            PARAMETER_INITIALIZE_PTR(modeBranch, modeList.Count());

            for (UINT32 i = 0; i < modeList.Count(); i++)
            {
                PARAMETER_MEMCPY(&modeBranch[i], sizeof(TuningMode), &modeList[i], sizeof(TuningMode));
            }
            result = GetInfo(modeBranch, modeList.Count(), path, info);

            PARAMETER_DELETE[] modeBranch;
        }
    }
    else
    {
        result = GetInfo(nullptr, 0, path, info);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetInfo
///
/// @brief Returns information about a specific parameter, or returns the list of parameters from the specified path
///
/// @param modeBranch   Mode branch. If nullptr or modeCount is 0, last accessed tuning mode branch will be used
/// @param modeCount    Mode count
/// @param path         Dot '.' separated Path to the variable to query.
///                     Example: cc14_ipr_v2.control_variables.control_var_type
/// @param __objects    list of objects
///
/// @return True path was found
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::GetInfo(
    const TuningMode*           modeBranch,
    UINT32                      modeCount,
    const CHAR* const           path,
    PList<ParameterParserInfo>& info)
{
    bool result = false;
    const ParameterMutexLock rlock {ReloadMutex, true, true}; // Shared LOCK

    if (modeBranch == nullptr || modeCount == 0)
    {
        PARAMETER_LOG_ERROR(this, "GetInfo called with null or empty modeBranch\n");
    }
    else
    {
        constexpr UINT32 MAX_MODULE = 65U;
        CHAR moduleName[MAX_MODULE];

        if (path != nullptr)
        {
            UINT32 const pos = ParameterUtils::GetSubString(path, '.', moduleName, MAX_MODULE);
            std::string const strName(moduleName);
            ParameterModule* const module = GetModule(moduleName, modeBranch, modeCount, nullptr);

            if (module != nullptr)
            {
                if (pos > 0 && pos + 2 < PARAMETER_STRLEN(path))
                {
                    result = module->GetInfo(&path[pos + 1], info);
                }
                else
                {
                    result = module->GetInfo(nullptr, info);
                }
            }
        }
        else
        {
            std::unordered_map<std::string, ModeEntry::ModuleReference>::iterator iter = m_rootMode->ModuleMap.begin();
            while (iter != m_rootMode->ModuleMap.end())
            {
                ParameterModule* const module = GetModule(iter->second.Module->Name, modeBranch, modeCount, nullptr);
                if (module != nullptr)
                {
                    if (module->Data != nullptr && module->Data->ModePath != nullptr)
                    {
                        CHAR modeString[MAX_MODE_PATH] = "";
                        GetEzTuneModesString(module->Data->ModePath, modeString, MAX_MODE_PATH);
                        info.Add(ParameterParserInfo(module->Name, ParameterParserDataType::Module, modeString));
                    }
                    else
                    {
                        info.Add(ParameterParserInfo(module->Name, ParameterParserDataType::Module));
                    }
                    result = true;
                }
                iter++;
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetActiveModes
///
/// @brief Returns the currently active tuning mode branches
///
/// @param path         Output Pipe '|' separated Mode path, multiple paths separated by a semi-colon ';'
/// @param pathSize     Size of path character array, recommended min size is MIN_PATH_SIZE, assuming 7 modes per branch
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::GetActiveModes(
    CHAR* const  path,
    UINT32 const pathSize)
{
    if (path != nullptr && pathSize > 0)
    {
        const UINT32 MAX_SIZE      = 1024;
        CHAR activeModes[MAX_SIZE] = "";
        const ParameterMutexLock mlock{ ModuleMutex, false, IsFileMode() }; // Unique LOCK

        SIZE_T pathLen = 0;
        path[0] = '\0';

        PList<ModeEntry*> sendModes;
        bool inactiveFound = false;
        for (UINT32 i = 0; i < m_activeModes.Count(); i++)
        {
            ModeEntry* mode = m_activeModes[i];
            if (mode->IsActive())
            {
                bool add = !inactiveFound || !mode->Preserve;
                for (UINT32 j = 0; add && j < sendModes.Count(); j++)
                {
                    // If this is a child of the sendModes object, then replace it
                    if (mode->IsParent(sendModes[j]))
                    {
                        sendModes[j] = mode;
                        add = false;
                    }
                    else if (sendModes[j]->IsParent(mode))
                    {
                        // If this mode is a parent of the sendModes object, then do not add it
                        add = !sendModes[j]->IsParent(mode);
                    }
                }

                if (add)
                {
                    sendModes.Add(mode);
                }
            }
            else
            {
                // once an inacive mode is found, assume any subsequent preserved mode is also inactive
                inactiveFound = true;
            }
        }

        for (UINT32 i = 0; i < sendModes.Count(); i++)
        {
            CHAR modePath[MAX_MODE_PATH] = "";
            ModeEntry* mode = sendModes[i];
            mode->GetModePath(modePath, MAX_MODE_PATH);
            SIZE_T len = PARAMETER_STRLEN(modePath);

            // Make sure there is enough room for a semi-colon and the nul terminator
            if (len + 2 < MAX_SIZE)
            {
                if (pathLen > 0)
                {
                    PARAMETER_STRCAT(activeModes, MAX_SIZE, ";");
                    pathLen++;
                }
                PARAMETER_STRCAT(activeModes, MAX_SIZE, modePath);
                pathLen += len;
            }
            else
            {
                break;
            }
        }

        GetEzTuneModesString(activeModes, path, pathSize);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetEzTuneModesString
///
/// @brief Converts mode paths into EzTune mode paths
///
/// @param input        Pipe '|' separated Mode path; ...
///                     Example: "0|1.0|2.0;0|1.0:2.1" (represents Default|Sensor.0|Preview;Default|Sensor.0|Snapshot)
/// @param output       Pipe '|' separated Mode path
///                     Example: "Default|Sensor.0|Usecase.Preview;Default|Sensor.0|Usecase.Snapshot"
/// @param length       Length of the output parameter
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::GetEzTuneModesString(
    const CHAR* input,
    CHAR*       output,
    UINT32      length)
{
    constexpr SIZE_T MAX_SUBSTRING = 512;

    UINT32 inputStart = 0;
    UINT32 pathLen;
    CHAR   modePath[MAX_SUBSTRING];
    UINT32 inputLen = (UINT32)PARAMETER_STRLEN(input);
    output[0] = '\0';

    do
    {
        if (inputStart > 0)
        {
            PARAMETER_STRCAT(output, length, ";");
        }
        pathLen = ParameterUtils::GetSubString(&input[inputStart], ';', modePath, MAX_SUBSTRING);
        UINT32 scenarioStart = 0;
        do
        {
            CHAR   scenario[MAX_SUBSTRING];
            UINT32 scenarioLen;
            scenarioLen = ParameterUtils::GetSubString(&modePath[scenarioStart], '|', scenario, MAX_SUBSTRING);

            CHAR   modeStr[MAX_SUBSTRING];
            UINT32 modeLen = ParameterUtils::GetSubString(scenario, '.', modeStr, MAX_SUBSTRING);
            UINT32 subModePos = modeLen;
            if (scenario[modeLen] == '.')
            {
                subModePos++;
            }

            if (scenarioStart > 0)
            {
                PARAMETER_STRCAT(output, length, "|");
            }

            ModeEntry entry(true, true);
            entry.Mode.Mode.Mode = (UINT16)ParameterUtils::StringToDouble(modeStr);
            const CHAR* mode = entry.GetModeName();
            if (mode != nullptr)
            {
                PARAMETER_STRCAT(output, length, mode);
            }

            entry.Mode.Mode.SubMode = (UINT16)ParameterUtils::StringToDouble(&scenario[subModePos]);
            const CHAR* subModeStr = entry.GetSubModeName();
            if (subModeStr != nullptr)
            {
                PARAMETER_STRCAT(output, length, ".");
                PARAMETER_STRCAT(output, length, subModeStr);
            }
            else if (entry.Mode.Mode.Mode > 0)
            {
                PARAMETER_STRCAT(output, length, &scenario[modeLen]);
            }

            scenarioStart += scenarioLen + 1;

        } while (scenarioStart < pathLen);

        inputStart += pathLen + 1;
    } while (inputStart < inputLen);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetModeShorthand
///
/// @brief Converts an EzTune mode path into a SetManager mode path
///
/// @param input        Pipe '|' separated EzTune Mode path
///                     Example: "Default|Sensor.0|Usecase.Preview"
/// @param output       Pipe '|' separated SetManager Mode path
///                     Example: "0|1.0|2.0" (represents Default|Sensor.0|Usecase.Preview)
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::GetModeShorthand(
    const CHAR* input,
    CHAR*       output,
    UINT32      length)
{
    constexpr SIZE_T MAX_SUBSTRING = 256;

    UINT32 scenarioLen;
    CHAR   scenario[MAX_SUBSTRING];
    UINT32 outputPos = 0;
    UINT32 start     = 0;
    UINT32 inputLen  = (UINT32)PARAMETER_STRLEN(input);

    if (length > 0)
    {
        do
        {
            scenarioLen = ParameterUtils::GetSubString(&input[start], '|', scenario, MAX_SUBSTRING);

            if (scenarioLen > 0)
            {
                if (start > 0 && outputPos + 1 < length)
                {
                    output[outputPos++] = '|';
                }

                CHAR   modeStr[MAX_SUBSTRING];
                UINT32 modeLen = ParameterUtils::GetSubString(scenario, '.', modeStr, MAX_SUBSTRING);

                UINT16 mode = GetModeIndex(modeStr);
                ParameterUtils::DoubleToString(mode, &output[outputPos], length - outputPos, 0);
                outputPos = (UINT32)PARAMETER_STRLEN(output);

                if (modeLen + 1 < scenarioLen)
                {
                    if (outputPos + 1 < length)
                    {
                        output[outputPos++] = '.';
                    }

                    UINT16 subMode = GetSubModeIndex(mode, &scenario[modeLen + 1]);
                    ParameterUtils::DoubleToString(subMode, &output[outputPos], length - outputPos, 0);
                    outputPos = (UINT32)PARAMETER_STRLEN(output);
                }

                start += scenarioLen + 1;
            }
        } while (start < inputLen);

        if (outputPos < length)
        {
            output[outputPos] = '\0';
        }
        output[length - 1] = '\0';
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetControlValues
///
/// @brief Returns control values, or null if not applicable, or not set by the camera system code
///
/// @param values   Output List of values
///
/// @return True if the control value(s) have been set and size > control value string length, else False
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::GetControlValues(
    PList<ParameterCTValue>& values)
{
    values.Clear();

    const ParameterMutexLock lock{ m_ctValuesMutex, false, true }; // Unique LOCK
    for (UINT32 i = 0; i < m_controlValues.Count(); i++)
    {
        values.Add(m_controlValues[i]);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SetControlValues
///
/// @brief Replaces all control values with the values specified in the control value array.
///        To replace only a subset of value, use the SetControlValue functions
///
/// @param values   Array of values
/// @param count    Number of elements in the value array
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::SetControlValues(
    const ParameterCTValue* const values,
    UINT32 const count)
{
    const ParameterMutexLock lock{ m_ctValuesMutex, false, true }; // Unique LOCK

    m_controlValues.Clear();

    if (values != nullptr)
    {
        for (UINT32 i = 0; i < count; i++)
        {
            SetControlValue(values[i]);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SetControlValues
///
/// @brief Replaces all control values with the values specified in the control value list.
///        To replace only a subset of value, use the SetControlValue functions
///
/// @param values   List of values
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::SetControlValues(
    const PList<ParameterCTValue>& values)
{
    const ParameterMutexLock lock{ m_ctValuesMutex, false, true }; // Unique LOCK

    m_controlValues.Clear();

    for (UINT32 i = 0; i < values.Count(); i++)
    {
        SetControlValue(values[i]);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetControlValues
///
/// @brief Returns control values, or nullptr if not applicable, or not set by the camera system code
///
/// @param values   Output Value(s) string / comma separated list of <control index>=<control value>
/// @param size     Size of the values buffer
///
/// @return True if the control value(s) have been set and size > control value string length, else False
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::GetControlValues(
    CHAR* const  values,
    UINT32 const size)
{
    bool valid = false;

    const ParameterMutexLock lock{ m_ctValuesMutex, false, true }; // Unique LOCK
    if (values != nullptr && size > 0 && m_controlValues.Count() > 0)
    {
        valid     = true;
        values[0] = '\0';
        SIZE_T valuesLen = 0;
        for (UINT32 i = 0; valid && i < m_controlValues.Count(); i++)
        {
            constexpr SIZE_T STR_SIZE = 128;
            CHAR valueStr[STR_SIZE];

            if (m_controlValues[i].ToString(valueStr, STR_SIZE, true))
            {
                SIZE_T const valueLen = PARAMETER_STRLEN(valueStr);
                valid = valuesLen + valueLen + 1 < size;
                if (valid)
                {
                    if (i > 0)
                    {
                        PARAMETER_STRCAT(values, size, ",");
                        valuesLen++;
                    }

                    PARAMETER_STRCAT(values, size, valueStr);
                    valuesLen += valueLen;
                }
            }
        }
    }

    return valid;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetControlValue
///
/// @brief Returns control value for the specified index
///
/// @param enumVal  Control enum value
/// @param values   Output Value
///
/// @return True if the control value index is found
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::GetControlValue(
    UINT32 const      enumVal,
    ParameterCTValue& value)
{
    bool result = false;

    const ParameterMutexLock lock{ m_ctValuesMutex, false, true }; // Unique LOCK
    for (UINT32 i = 0; !result && i < m_controlValues.Count(); i++)
    {
        if (m_controlValues[i].EnumVal == enumVal)
        {
            value = m_controlValues[i];
            result = true;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SetControlValues
///
/// @brief Replaces all control values with the values specified in the string.
///        To replace only a subset of value, use SetControlValue functions
///
/// @param values   Value(s) string / comma separated list of <control enum value>=<control value> pairs
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::SetControlValues(
    const CHAR* const values)
{
    const ParameterMutexLock lock{ m_ctValuesMutex, false, true }; // Unique LOCK

    m_controlValues.Clear();

    if (values != nullptr)
    {
        const SIZE_T length = PARAMETER_STRLEN(values);

        UINT32 pos = 0;
        while (pos < length)
        {
            constexpr SIZE_T STR_SIZE = 64;
            CHAR valueStr[STR_SIZE];
            pos += ParameterUtils::GetSubString(&values[pos], ',', valueStr, STR_SIZE);
            ParameterCTValue const value(valueStr);
            SetControlValue(value);
            pos++;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetControlValue
///
/// @brief Returns control value for the specified index
///
/// @param enumVal  Control enum value
/// @param values   Output Value
///
/// @return True if the control value index is found
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::SetControlValue(
    const ParameterCTValue& value)
{
    const ParameterMutexLock lock{ m_ctValuesMutex, false, true }; // Unique LOCK

    INT32 const count = (INT32)m_controlValues.Count();
    INT32 insert = count;
    for (INT32 i = 0; insert == count && i < count; i++)
    {
        if (m_controlValues[i].EnumVal == value.EnumVal)
        {
            m_controlValues[i] = value;
            insert = -1;
        }
        else if (m_controlValues[i].EnumVal > value.EnumVal)
        {
            insert = i;
        }
    }

    if (insert >= 0)
    {
        m_controlValues.Insert(insert, value);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetTriggerValues
///
/// @brief Returns trigger values, or null if not applicable, or not set by the camera system code
///
/// @param values   Output List of values
///
/// @return True if the trigger value(s) have been set and size > trigger value string length, else False
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::GetTriggerValues(
    PList<ParameterCTValue>& values)
{
    values.Clear();

    const ParameterMutexLock lock{ m_ctValuesMutex, false, true }; // Unique LOCK
    for (UINT32 i = 0; i < m_triggerValues.Count(); i++)
    {
        values.Add(m_triggerValues[i]);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SetTriggerValues
///
/// @brief Replaces all trigger values with the values specified in the trigger value array.
///        To replace only a subset of value, use the SetTriggerValue functions
///
/// @param values   Array of values
/// @param count    Number of elements in the value array
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::SetTriggerValues(
    const ParameterCTValue* const values,
    UINT32 const count)
{
    const ParameterMutexLock lock{ m_ctValuesMutex, false, true }; // Unique LOCK

    m_triggerValues.Clear();

    if (values != nullptr)
    {
        for (UINT32 i = 0; i < count; i++)
        {
            SetTriggerValue(values[i]);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SetTriggerValues
///
/// @brief Replaces all trigger values with the values specified in the trigger value list.
///        To replace only a subset of value, use the SettriggerValue functions
///
/// @param values   List of values
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::SetTriggerValues(
    const PList<ParameterCTValue>& values)
{
    const ParameterMutexLock lock{ m_ctValuesMutex, false, true }; // Unique LOCK

    m_triggerValues.Clear();

    for (UINT32 i = 0; i < values.Count(); i++)
    {
        SetTriggerValue(values[i]);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetTriggerValues
///
/// @brief Returns trigger values, or nullptr if not applicable, or not set by the camera system code
///
/// @param values   Output Value(s) string / comma separated list of <trigger enum value index>=<trigger value>
/// @param size     Size of the values buffer
///
/// @return True if the trigger value(s) have been set and size > trigger value string length, else False
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::GetTriggerValues(
    CHAR* const  values,
    UINT32 const size)
{
    bool valid = false;

    const ParameterMutexLock lock{ m_ctValuesMutex, false, true }; // Unique LOCK
    if (values != nullptr && size > 0 && m_triggerValues.Count() > 0)
    {
        valid = true;
        values[0] = '\0';
        SIZE_T valuesLen = 0;
        for (UINT32 i = 0; valid && i < m_triggerValues.Count(); i++)
        {
            constexpr SIZE_T STR_SIZE = 128;
            CHAR valueStr[STR_SIZE];

            if (m_triggerValues[i].ToString(valueStr, STR_SIZE, true))
            {
                SIZE_T const valueLen = PARAMETER_STRLEN(valueStr);
                valid = valuesLen + valueLen + 1 < size;
                if (valid)
                {
                    if (i > 0)
                    {
                        PARAMETER_STRCAT(values, size, ",");
                        valuesLen++;
                    }

                    PARAMETER_STRCAT(values, size, valueStr);
                    valuesLen += valueLen;
                }
            }
        }
    }

    return valid;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetTriggerValue
///
/// @brief Returns trigger value for the specified enum value
///
/// @param enumVal  Trigger enum value
/// @param values   Output Value
///
/// @return True if the trigger enum value is found
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::GetTriggerValue(
    UINT32 const           enumVal,
    ParameterCTValue& value)
{
    bool result = false;

    const ParameterMutexLock lock{ m_ctValuesMutex, false, true }; // Unique LOCK
    for (UINT32 i = 0; !result && i < m_triggerValues.Count(); i++)
    {
        if (m_triggerValues[i].EnumVal == enumVal)
        {
            value = m_triggerValues[i];
            result = true;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SetTriggerValues
///
/// @brief Replaces all trigger values with the values specified in the string.
///        To replace only a subset of value, use SetTriggerValue functions
///
/// @param values   Value(s) string / comma separated list of <trigger enum value>=<trigger value> pairs
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::SetTriggerValues(
    const CHAR* const values)
{
    const ParameterMutexLock lock{ m_ctValuesMutex, false, true }; // Unique LOCK

    m_triggerValues.Clear();

    if (values != nullptr)
    {
        const SIZE_T length = PARAMETER_STRLEN(values);

        UINT32 pos = 0;
        while (pos < length)
        {
            constexpr SIZE_T STR_SIZE = 64;
            CHAR valueStr[STR_SIZE];
            pos += ParameterUtils::GetSubString(&values[pos], ',', valueStr, STR_SIZE);
            ParameterCTValue const value(valueStr);
            SetTriggerValue(value);
            pos++;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SetTriggerValue
///
/// @brief Replaces the trigger value with the value specified.
///
/// @param value    Trigger enum value and value
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::SetTriggerValue(
    const ParameterCTValue& value)
{
    const ParameterMutexLock lock{ m_ctValuesMutex, false, true }; // Unique LOCK

    INT32 const count = (INT32)m_triggerValues.Count();
    INT32 insert = count;
    for (INT32 i = 0; insert == count && i < count; i++)
    {
        if (m_triggerValues[i].EnumVal == value.EnumVal)
        {
            m_triggerValues[i] = value;
            insert = -1;
        }
        else if (m_triggerValues[i].EnumVal > value.EnumVal)
        {
            insert = i;
        }
    }

    if (insert >= 0)
    {
        m_triggerValues.Insert(insert, value);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetChildModes
///
/// @brief Returns a nul terminated, comma separated list of child modes from the given mode path.
///
/// @param requestPath     Pipe '|' separated Mode path
///                        Example: "0|1.0|2.0" (represents Default|Sensor.0|Preview)
/// @param responsePath    Comma ',' separated, and nul terminated child modes string
///                        Example: "1.0,1.1,1.2" (represents Preview,Snapshot,Video)
/// @param responseSize    Size of the responsePath array
///
/// @return Number of characters written including the nul terminator
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT32 ParameterSetManager::GetChildModes(
    const CHAR* requestPath,
    BYTE*       responsePath,
    UINT32      responseSize)
{
    ModeEntry* entry = GetModeEntry(requestPath);

    if (entry == nullptr)
    {
        // No mode specified, get the default mode
        entry = GetModeEntry((UINT32)0);
    }

    UINT32 pos = 0;
    if (entry != nullptr)
    {
        ModeEntry* child = entry->FirstChild;
        while (child != nullptr)
        {
            ParameterUtils::WriteString(child->GetModeName(), responsePath, responseSize, &pos, false, this);
            if (child->Mode.Mode.Mode != 0)
            {
                ParameterUtils::Write((BYTE)'.', (BYTE*)responsePath, responseSize, &pos, false, this);
                responsePath[pos] = '\0';

                const CHAR* subMode = child->GetSubModeName();
                if (subMode == nullptr)
                {
                    ParameterUtils::DoubleToString(child->Mode.Mode.SubMode, (CHAR*)&responsePath[pos], responseSize - pos, 0);
                    pos = (UINT32)PARAMETER_STRLEN((CHAR*)responsePath);
                }
                else
                {
                    ParameterUtils::WriteString(subMode, responsePath, responseSize, &pos, false, this);
                }
            }

            child = child->Next;

            if (child != nullptr)
            {
                ParameterUtils::Write((BYTE)',', responsePath, responseSize, &pos, false, this);
                responsePath[pos] = '\0';
            }
        }
    }

    ParameterUtils::Write((BYTE)'\0', responsePath, responseSize, &pos, false, this);

    return pos;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// FreeReplacedParameters
///
/// @brief Frees any Parameter objects that were replaced with a call to UpdateParameters
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::FreeReplacedParameters()
{
    PARAMETER_LOG_DEBUG(this, "FreeReplacedParameters called\n");
    while (m_replaced.size() > 0)
    {
        ParameterModule* const replaced = m_replaced.front();
        m_replaced.pop_front();
        PARAMETER_DELETE replaced;
    }

    while (m_replacedST.Count() > 0)
    {
        ParameterFileSymbolTable* const replaced = m_replacedST[0];
        m_replacedST.RemoveAt(0);
        PARAMETER_DELETE replaced;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// LoadMode
///
/// @brief Loads the specified scenario mode
///
/// @param modeBranch   Mode branch
/// @param modeCount    Mode count
/// @param caller       Name of calling function (for logging purposes)
///
/// @return Returns false if the specified scenario mode could not be found
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::LoadMode(
    const TuningMode* const modeBranch,
    UINT32 const            modeCount,
    const CHAR* const       caller)
{
    bool       reload   = false;
    bool       result   = false;
    bool const fileMode = IsFileMode();

    if (SymbolTable == nullptr)
    {
        SetError("LoadMode called with no binary file loaded");
    }
    else if (fileMode)
    {
        bool reloaded; // Only allow a single reload attempt, otherwise fail

        const ParameterMutexLock mlock {ModuleMutex, false, true}; // Unique LOCK

        do
        {
            reloaded = reload;
            result   = LoadModeInternal(modeBranch, modeCount, reload, caller);
            if (reload)
            {
                (void)ReloadBinaryParameters(false);
            }
        } while (reload && !reloaded);
    }
    else
    {
        SetError("LoadMode is not valid in buffer mode");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// UnloadMode
///
/// @brief Unloads the specified scenario mode
///
/// @param modeBranch   Mode branch
/// @param modeCount    Mode count
///
/// @return Returns false if the specified scenario mode could not be found
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::UnloadMode(
    const TuningMode* const modeBranch,
    UINT32 const            modeCount)
{
    ModeEntry* mode     = m_rootMode;
    bool const fileMode = IsFileMode();

    if (!fileMode)
    {
        SetError("UnloadMode is not valid in buffer mode");
    }
    else if (mode == nullptr)
    {
        SetError("UnloadMode root mode is nullptr");
    }
    else
    {
        // First mode must always been root, start at index 1
        UINT32 index              = 1;

        while (mode != nullptr && index < modeCount)
        {
            UINT32 sameModeCount = 1;

            while (index + sameModeCount < modeCount && modeBranch[index].mode == modeBranch[index + sameModeCount].mode)
            {
                sameModeCount++;
            }

            mode = mode->FindMode(&modeBranch[index], sameModeCount, 0);

            index += sameModeCount;
        }

        if (mode != nullptr)
        {
            INT32 fileRefs = 0; // Unloading does not add a file reference, so we can ignore the ref count
            (void)LoadModules(mode, false, nullptr, modeBranch, modeCount, fileRefs);
        }
    }

    return mode != nullptr;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetModule
///
/// @brief Returns first parameter module matching the name, mode, selector, and selectorData
///
/// @param name         Module name
/// @param modeBranch   Mode branch
/// @param modeCount    Mode count
/// @param release      If not nullptr, Release() is called on the specified module
///
/// @return The first parameter module found matching the name and mode.  Version is ignored.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ParameterModule* ParameterSetManager::GetModule(
    const CHAR* const       name,
    const TuningMode* const modeBranch,
    UINT32 const            modeCount,
    ParameterModule* const  release)
{
    ParameterModule* result = nullptr;

    const ParameterMutexLock rlock {ReloadMutex, true, true}; // Shared LOCK

    if (name == nullptr)
    {
        SetError("GetModule called with nullptr module name");
    }
    else if (m_rootMode != nullptr)
    {
        if (modeBranch == nullptr && modeCount > 0)
        {
            PARAMETER_LOG_ERROR(this, "GetModule(%s) called with modeBranch == null and modeCount = %d, will return default mode module data\n", name, modeCount);
        }
        else if (modeBranch != nullptr && modeCount > 0 && (modeBranch[0].mode != ModeType::Default || modeBranch[0].subMode.value != 0))
        {
            CHAR modePath[MAX_MODE_PATH] = "";
            GetPathFromModeBranch(modeBranch, modeCount, modePath, MAX_MODE_PATH);
            PARAMETER_LOG_ERROR(this, "GetModule(%s) called with first mode != Default, might not return module data from the desired mode path: %s\n",
               name, modePath);
        }

        if (DefaultModuleMap.size() == 0)
        {
#ifdef _WIN32
            PARAMETER_LOG_ERROR(this, "GetModule(%s) Uninitialized Set Manager. this: %llx, SymbolTable: %llx, Modes: %llx, ModeCount: %d, RootMode: %llx\n",
                name, (SIZE_T)this, (SIZE_T)SymbolTable, (SIZE_T)m_modes, m_modeCount, (SIZE_T)m_rootMode);
#else
            PARAMETER_LOG_ERROR(this, "GetModule(%s) Uninitialized Set Manager. this: %lx, SymbolTable: %lx, Modes: %lx, ModeCount: %d, RootMode: %lx\n",
                name, (SIZE_T)this, (SIZE_T)SymbolTable, (SIZE_T)m_modes, m_modeCount, (SIZE_T)m_rootMode);
#endif
        }
        std::string strName(name);

        if (IsFileMode())
        {
            (void)LoadMode(modeBranch, modeCount, name);
        }

        ModeEntry* mode = m_rootMode;

        result = mode->FindModule(&strName);

        if (result == nullptr)
        {
            CHAR rootPath[MAX_MODE_PATH] = "";
            CHAR modePath[MAX_MODE_PATH] = "";
            GetPathFromModeBranch(modeBranch, modeCount, modePath, MAX_MODE_PATH);
            m_rootMode->GetModePath(rootPath, MAX_MODE_PATH);
            PARAMETER_LOG_WARNING(this, "GetModule %s module not found in default scenario (%s), mode path: %s\n", name, rootPath, modePath);
        }
        else
        {
            const ParameterMutexLock mlock{ ModuleMutex, false, true }; // Unique LOCK
            if (!result->Loaded)
            {
                (void)result->LoadModule(SymbolTable);
            }
        }

        // First mode must always be root, start at index 1
        UINT32 index = 1;
        while (mode != nullptr && modeBranch != nullptr && index < modeCount)
        {
            UINT32 sameModeCount = 1;

            while (index + sameModeCount < modeCount && modeBranch[index].mode == modeBranch[index + sameModeCount].mode)
            {
                sameModeCount++;
            }

            mode = mode->FindMode(&modeBranch[index], sameModeCount, 0);

            if (mode != nullptr)
            {
                ParameterModule* const module = mode->FindModule(&strName);

                if (module != nullptr)
                {
                    bool loaded;
                    {
                        const ParameterMutexLock mlock{ ModuleMutex, false, true }; // Unique LOCK
                        loaded = module->Loaded;

                        if (!loaded)
                        {
                            loaded = module->LoadModule(SymbolTable);
                        }

                        if (loaded)
                        {
                            result = module;
                        }
                    }
                    if (!loaded)
                    {
                        PARAMETER_LOG_ERROR(this, "Failed to load module %s, path: %s, skipping\n",
                            module->Name, module->Data->ModePath);
                    }
                }
            }

            index += sameModeCount;
        }

        if (IsFileMode() && result != nullptr)
        {
            // Add reference
            if (result->Data != nullptr)
            {
                if (result->Data->Mode != nullptr)
                {
                    result->Data->Mode->AddReference(result);
                }
                else
                {
                    PARAMETER_LOG_ERROR(this, "Attempt to add reference to module with a nullptr Data->Mode object.  Mode: %d, SubMode: %d, Module %s\n",
                        result->Mode.Mode.Mode, result->Mode.Mode.SubMode, result->Name);
                }
            }
            else
            {
                PARAMETER_LOG_ERROR(this, "Attempt to add reference to module with a nullptr Data object.  Mode: %d, SubMode: %d, Module %s\n",
                    result->Mode.Mode.Mode, result->Mode.Mode.SubMode, result->Name);
            }
        }
    }
    else
    {
        PARAMETER_LOG_ERROR(this, "GetModule %s called with no binary loaded\n", name);
    }

    if (release != nullptr)
    {
        release->Release();
    }

    if (result != nullptr && !result->Loaded)
    {
        CHAR modePath[MAX_MODE_PATH] = "";
        GetPathFromModeBranch(modeBranch, modeCount, modePath, MAX_MODE_PATH);

        PARAMETER_LOG_ERROR(this, "GetModule %s failed to find a loaded module in %s, returning null\n", name, modePath);
        result = nullptr;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ReleaseModule
///
/// @brief Releases reference to the module.  When used in conjunction with the file based LoadBinaryParameters function
///        Will automatically call UnloadMode on modes once all it's module references are released, as long as the
///        MinimumModes value is satisfied
///
/// @param module       ParameterModule object
///
/// @return The first parameter module found matching the name and mode.  Version is ignored.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::ReleaseModule(
    const ParameterModule* const module)
{
    const ParameterMutexLock mlock {ModuleMutex, true, true}; // Shared LOCK

    if (module != nullptr)
    {
        if (module->Data != nullptr)
        {
            if (module->Data->Mode != nullptr)
            {
                module->Data->Mode->ReleaseReference(module);
            }
            else
            {
                PARAMETER_LOG_ERROR(this, "Attempt to release module with a nullptr Data->Mode object.  Mode: %d, SubMode: %d, Module %s\n",
                    module->Mode.Mode.Mode, module->Mode.Mode.SubMode, module->Name);
            }
        }
        else
        {
            PARAMETER_LOG_ERROR(this, "Attempt to release module with a nullptr Data object.  Mode: %d, SubMode: %d, Module %s\n",
                module->Mode.Mode.Mode, module->Mode.Mode.SubMode, module->Name);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetModeEntry
///
/// @brief Gets the ModeEntry for a given Mode ID
///
/// @param modeID    Mode ID
///
/// @return ModeEntry entry
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ModeEntry* ParameterSetManager::GetModeEntry(
    UINT32 const modeID)
{
    ModeEntry* result = nullptr;

    const ParameterMutexLock rlock {ReloadMutex, true, true}; // Shared LOCK

    if (modeID < m_modeCount)
    {
        result = m_modes[modeID];
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetModeEntry
///
/// @brief Gets the Mode and SubMode from a given Mode path
///
/// @param modePath     Pipe '|' separated Mode path
///                     Example: "0|1.0|2.0" (represents Default|Sensor.0|Preview)
///
/// @return ModeEntry if found, otherwise nullptr
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ModeEntry* ParameterSetManager::GetModeEntry(
    const CHAR* const modePath)
{
    ModeEntry* result = nullptr;

    if (modePath != nullptr)
    {
        PList<TuningMode> modeList;

        bool const valid = GetModeBranchFromPath(modePath, modeList);

        if (valid)
        {
            TuningMode* modeBranch = PARAMETER_NEW TuningMode[modeList.Count()];
            PARAMETER_INITIALIZE_PTR(modeBranch, modeList.Count());

            for (UINT32 i = 0; i < modeList.Count(); i++)
            {
                PARAMETER_MEMCPY(&modeBranch[i], sizeof(TuningMode), &modeList[i], sizeof(TuningMode));
            }
            result = GetModeEntry(modeBranch, modeList.Count());

            PARAMETER_DELETE[] modeBranch;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetModeEntry
///
/// @brief Gets the Mode and SubMode from a given Mode ID
///
/// @param modeBranch   Mode branch
/// @param modeCount    Mode count
///
/// @return ModeEntry if found, otherwise nullptr
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ModeEntry* ParameterSetManager::GetModeEntry(
    const TuningMode* const modeBranch,
    UINT32 const            modeCount)
{
    ModeEntry* result = nullptr;

    const ParameterMutexLock rlock {ReloadMutex, true, true}; // Shared LOCK

    if (m_rootMode != nullptr)
    {
        ModeEntry* mode = m_rootMode;

        if (IsFileMode())
        {
            (void)LoadMode(modeBranch, modeCount, "EzTune->GetModeEntry");
        }

        // First mode must always been root, start at index 1
        UINT32 index = 1;
        while (modeBranch != nullptr && mode != nullptr && index < modeCount)
        {
            UINT32 sameModeCount = 1;

            while (index + sameModeCount < modeCount && modeBranch[index].mode == modeBranch[index + sameModeCount].mode)
            {
                sameModeCount++;
            }

            mode = mode->FindMode(&modeBranch[index], sameModeCount, 0);

            index += sameModeCount;
        }

        result = mode;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ParameterSetManager
///
/// @brief Constructor
///
/// @return Node
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ParameterSetManager::ParameterSetManager()
{
    BinaryTag       = nullptr;
    Valid           = false;
    Error[0]        = '\0';
    Flags           = 0;
    SymbolTable     = nullptr;
    MinimumModes    = 0;
    DelayLoad       = false;
    m_rootMode      = nullptr;
    m_modes         = nullptr;
    m_modeCount     = 0;
    Sections        = 0;
    SymbolSize      = 0;
    DataTableOffset = 0;
    DataTableSize   = 0;
    ModeOffset      = 0;
    ModeSize        = 0;
    DataSize        = 0;
    DataOffset      = 0;
    BinarySize      = 0;
    LogError        = true;
    LogWarning      = true;
    LogDebug        = true;
    SimulatorMode   = false;
    EzTuneAuto      = true;
    EzTuneUpdated   = 0;
#ifdef EZTUNE
    m_ezTune        = nullptr;
#endif

    PARAMETER_INITIALIZE(m_file);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ~ParameterSetManager
///
/// @brief Destructor
///
/// @return Node
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ParameterSetManager::~ParameterSetManager()
{
    PARAMETER_LOG_DEBUG(this, "ParameterSetManager deleting\n");
    FreeReplacedParameters();
    FreeBinaryParameters();

#ifdef EZTUNE
    if (m_ezTune != nullptr)
    {
        m_ezTune->Stop();
        PARAMETER_DELETE m_ezTune;
    }
#endif

    while (m_updateSM.size() > 0)
    {
        ParameterSetManager* const update = m_updateSM.front();
        m_updateSM.pop_front();
        PARAMETER_DELETE update;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// LoadModeInternal
///
/// @brief Loads or unloads the specified scenario mode
///
/// @param modeBranch   Mode branch
/// @param modeCount    Mode count
/// @param reload[out]  Set to true if the entire symbol table should be reloaded
/// @param caller       What action triggered the load/unload, used in diagnostic and error messages
///
/// @return Returns false if the specified scenario mode could not be found
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::LoadModeInternal(
    const TuningMode* const modeBranch,
    UINT32 const            modeCount,
    bool&                   reload,
    const CHAR* const       caller)
{
    ModeEntry* mode = m_rootMode;

    if (mode != nullptr)
    {
        // Load default mode
        INT32 fileRefs      = 0;
        bool const fileMode = IsFileMode();
        reload = !LoadModules(mode, true, caller, modeBranch, modeCount, fileRefs) && fileMode;

        // Load any remaining modes
        UINT32 index              = 1;
        while (modeBranch != nullptr && !reload && mode != nullptr && index < modeCount)
        {
            UINT32 sameModeCount = 1;

            while (index + sameModeCount < modeCount && modeBranch[index].mode == modeBranch[index + sameModeCount].mode)
            {
                sameModeCount++;
            }

            mode = mode->FindMode(&modeBranch[index], sameModeCount, 0);

            if (mode != nullptr)
            {
                reload = !LoadModules(mode, true, caller, modeBranch, modeCount, fileRefs) && fileMode;
            }

            index += sameModeCount;
        }

        // If in delay load mode (File != nullptr) and this mode branch has not been loaded yet,
        // SymbolTable->Data will be allocated, need to free it here
        if (SymbolTable->File != nullptr && SymbolTable->Data != nullptr)
        {
            PARAMETER_DELETE SymbolTable->Data;
            SymbolTable->Data = nullptr;
        }

        // Remove file references added when calling LoadModules
        if (fileRefs > 0)
        {
            SymbolTable->FileReference(-fileRefs);
        }
    }

    return mode != nullptr;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// UpdateParameters
///
/// @brief Replaces existing parameters.
///
/// @param parameters   ParameterSetManager object to use for replacement parameters. Parameters are removed from this object.
///
/// @return True if no errors occur
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::UpdateParameters(
    ParameterSetManager* const parameters)
{
    bool result = false;

    if (Valid && parameters != nullptr && parameters->m_rootMode != nullptr)
    {
        result = UpdateParameters(m_rootMode, parameters->m_rootMode, parameters);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// UpdateParameters
///
/// @brief Replaces existing parameters.
///
/// @param parameters   ParameterSetManager object to use for replacement parameters. Parameters are removed from this object.
///
/// @return True if no errors occur
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::UpdateParameters(
    ModeEntry* const           destinationRoot,
    ModeEntry* const           sourceRoot,
    ParameterSetManager* const sourceSM)
{
    bool result = false;

    if (destinationRoot != nullptr && sourceRoot != nullptr)
    {
        ModeEntry* sourceGroup = sourceRoot;

        result = true;
        while (result && sourceGroup != nullptr)
        {
            if (sourceGroup->ModuleMap.size() > 0)
            {
                // This source group contains modules, replace them in the matching destination group

                // Find matching destination group
                ModeEntry* const destinationGroup = destinationRoot;

                if (destinationGroup != nullptr && destinationGroup->Link == nullptr) // Linked destination nodes may not be replaced
                {
                    // Replace modules

                    while (sourceGroup->ModuleMap.size() > 0)
                    {
                        ParameterModule* const newModule = sourceGroup->ModuleMap.begin()->second.Module;
                        sourceGroup->ModuleMap.erase(newModule->Name);

                        std::unordered_map<std::string, DefaultModule*>::iterator iter = sourceSM->DefaultModuleMap.find(newModule->Type);
                        if (iter != sourceSM->DefaultModuleMap.end())
                        {
                            iter->second->RemoveModule(newModule);
                        }

                        ParameterModule* const replaced = destinationGroup->ReplaceModule(newModule);
                        if (replaced != nullptr)
                        {
                            iter = DefaultModuleMap.find(replaced->Type);
                            if (iter != DefaultModuleMap.end())
                            {
                                iter->second->RemoveModule(replaced);
                                iter->second->AddModule(newModule);
                            }

                            m_replaced.push_back(replaced);
                        }
                        else
                        {
                            PARAMETER_DELETE newModule;
                        }
                    }

                    result = true;
                }
            }

            // Move to the next source group
            sourceGroup = sourceGroup->Next;
        }

        result = true;

        // Process the child modes
        ModeEntry* sourceChild = sourceRoot->FirstChild;
        while (result && sourceChild != nullptr)
        {
            // Find matching destination root child
            TuningMode tm;
            tm.mode          = (ModeType)sourceChild->Mode.Mode.Mode;
            tm.subMode.value = sourceChild->Mode.Mode.SubMode;
            ModeEntry* const destinationChild = destinationRoot->FindMode(&tm);

            result = UpdateParameters(destinationChild, sourceChild, sourceSM);

            sourceChild = sourceChild->Next;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ReadHeader
///
/// @brief Reads and validates the binary parameter file header
///
/// @param buffer         Binary parameter buffer
/// @param length         Length of the binary parameter buffer
/// @param pos            Current position in the binary parameter buffer [in/out]
/// @param offset         Offset to the section buffer [out]
/// @param count          Section count [out]
/// @param alignment      Binary data alignment [out]
///
/// @return True if successful
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::ReadHeader(
    BYTE* const   buffer,
    UINT32        length,
    UINT32* const pos,
    UINT32* const offset,
    BYTE* const   alignment)
{
    bool         result    = true;
    CHAR*        tag       = nullptr;
    CHAR*        tool      = nullptr;
    UINT32       size      = 0;
    CHAR         error[ERROR_LEN];

    // Header alignment is always 1 for backwards compatibility with older binary formats
    error[0] = '\0';
    tag      = ParameterUtils::ReadString(buffer, length, pos, TAG_LEN, 1);

    result   = ParameterUtils::Read(&size, buffer, length, pos, 1, this);
    if (result)
    {
        result = ParameterUtils::Read(&Version.Value, buffer, length, pos, 1, this);
    }

    VersionUnion const ModeTreeMinVersion(MODE_TREE_MAJOR, MODE_TREE_MINOR, MODE_TREE_PATCH);
    VersionUnion const MaxVersion(MAJOR, MINOR, PATCH);
    VersionUnion const AlignedVersionStart(ALIGNED_START_MAJOR, ALIGNED_START_MINOR, ALIGNED_START_PATCH);
    VersionUnion const AlignedVersionEnd(ALIGNED_END_MAJOR, ALIGNED_END_MINOR, ALIGNED_END_PATCH);
    VersionUnion const FlagsVersion(FLAGS_MAJOR, FLAGS_MINOR, FLAGS_PATCH);

    if (Version >= ModeTreeMinVersion)
    {
        tool      = ParameterUtils::ReadString(buffer, length, pos, TOOL_LEN, 1);
        BinaryTag = ParameterUtils::ReadString(buffer, length, pos, BINARY_TAG_LEN, 1);
        PARAMETER_LOG_DEBUG(this, "ReadHeader: Version %d.%d.%d BinaryTag: %s, Tool: %s\n",
            Version.Version.Major, Version.Version.Minor, Version.Version.Patch, BinaryTag, tool);
    }
    else
    {
        PARAMETER_LOG_DEBUG(this, "ReadHeader: Version %d.%d.%d < ModeTreeMinVersion, BinaryTag not set\n",
            Version.Version.Major, Version.Version.Minor, Version.Version.Patch);
    }
    if (result && (Version >= FlagsVersion))
    {
        result = ParameterUtils::Read(&Flags, buffer, length, pos, 1, this);
    }

    if (result)
    {
        result = ParameterUtils::Read(offset,   buffer, length, pos, 1, this);
    }
    if (result)
    {
        result = ParameterUtils::Read(&Sections, buffer, length, pos, 1, this);
    }

    if (result)
    {
        if (tag == nullptr)
        {
            PARAMETER_SPRINTF(error, ERROR_LEN, "Failed to read binary header tag");
            SetError(error);
            result = false;
        }
        else if (PARAMETER_STRCMP(tag, TAG) != 0)
        {
            PARAMETER_SPRINTF(error, ERROR_LEN, "Invalid binary header tag: %s", tag);
            SetError(error);
            result = false;
        }
        else if (size > length && (Flags & FLAGS_COMPRESSED) == 0)
        {
            PARAMETER_SPRINTF(error, ERROR_LEN, "Binary header data too small: Expected: %d bytes, Actual: %u bytes", size, (UINT32)length);
            SetError(error);
            result = false;
        }
        else if (Version < ModeTreeMinVersion)
        {
            PARAMETER_SPRINTF(error, ERROR_LEN, "Warning: This is an older binary format, only Default mode data is supported.\n"
                "To support multiple modes, please re-generate binary using the latest version of Chromatix");
            SetError(error);
        }
        else if (Version > MaxVersion)
        {
            PARAMETER_SPRINTF(error, ERROR_LEN, "Unsupported binary header version : %u.%u.%u. Please re-generate parser code using the latest version of ParameterParser",
                Version.Version.Major, Version.Version.Minor, Version.Version.Patch);
            SetError(error);
            result = false;
        }
    }
    else
    {
        SetError("General error parsing binary header data header");
    }

    // Versions 1.0.x are 1-byte packed
    // Versions 1.1.0 and later are 8 byte aligned
    *alignment = Version < AlignedVersionStart || Version >= AlignedVersionEnd ? 1 : 8;

    if (result)
    {
        result = ParameterBinaryFile::ValidateCRC(buffer, length, this);
        if (!result)
        {
            SetError(Error);
        }
    }

    PARAMETER_DELETE[] tag;
    PARAMETER_DELETE[] tool;

    return result;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// AddModule
///
/// @brief Updates the module in the module table, or adds new module if not found.
///
/// @param module   Parameter module to add of update
///
/// @return Node
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::AddModule(
    const ModuleData* const moduleData,
    ParameterModule* const module)
{
    bool result = false;

    if (m_rootMode == nullptr)
    {
        SetError("AddModule: m_rootMode is nullptr");
    }
    else if (module == nullptr)
    {
        if (!SimulatorMode)
        {
            SetError("AddModule: module is nullptr");
        }
    }
    else if (moduleData->ModeId < m_modeCount)
    {
        (void)m_modes[moduleData->ModeId]->AddModule(module);
        result = true;
    }
    else
    {
        PARAMETER_LOG_ERROR(this, "AddModule: moduleData->ModeId(%d) >= m_modeCount(%d)\n", moduleData->ModeId, m_modeCount);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ReadSectionInfo
///
/// @brief Reads and validates the binary parameter file section table
///
/// @param buffer   Binary parameter buffer
/// @param length   Length of the binary parameter buffer
/// @param pos      Current position in the binary parameter buffer [in/out]
/// @param alignment Binary data alignment [out]
///
/// @return True if successful
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::ReadSectionInfo(
    BYTE* const   buffer,
    UINT32 const  length,
    UINT32* const pos,
    BYTE const    alignment)
{
    bool result = false;

    if (Sections < MIN_SECTIONS)
    {
        PARAMETER_LOG_ERROR(this, "ReadSectionInfo: Section count %d is less than Min sections %d\n", Sections, MIN_SECTIONS);
    }
    else if (Sections > MAX_SECTIONS)
    {
        PARAMETER_LOG_ERROR(this, "ReadSectionInfo: Section count %d is greater than Max sections %d\n", Sections, MAX_SECTIONS);
    }
    else if (buffer == nullptr)
    {
        SetError("ReadSectionInfo: buffer is nullptr");
    }
    else
    {
        result = true;
        for (UINT32 i = 0; result && i < Sections; i++)
        {
            UINT32 type   = 0;
            UINT32 offset = 0;
            UINT32 size   = 0;

            result = ParameterUtils::Read(&type,   buffer, length, pos, alignment, this);
            if (result)
            {
                result = ParameterUtils::Read(&offset, buffer, length, pos, alignment, this);
            }
            if (result)
            {
                result = ParameterUtils::Read(&size,   buffer, length, pos, alignment, this);
            }

            if (!result)
            {
                SetError("Error reading data from header sections");
                Valid = false;
            }
            else if (type == SYMBOL_SECTION)
            {
                SymbolSize   = size;
                SymbolOffset = offset;
            }
            else if (type == DATA_SECTION)
            {
                DataSize   = size;
                DataOffset = offset;
            }
            else if (type == MODE_SECTION)
            {
                ModeSize   = size;
                ModeOffset = offset;
            }
            else if (type == DATA_TABLE_SECTION)
            {
                DataTableSize   = size;
                DataTableOffset = offset;
            }
            else
            {
                PARAMETER_LOG_ERROR(this, "Invalid binary section type %d\n", type);
                Valid  = false;
                result = false;
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ReadSections
///
/// @brief Reads and validates the binary parameter file section table
///
/// @param buffer     Binary parameter buffer
/// @param bufLength  Length of the binary parameter buffer
/// @param fileName   Binary parameter file name
/// @param file       Binary parameter file pointer
/// @param fileLength Length of the binary parameter file
/// @param pos        Current position in the binary parameter buffer [in/out]
/// @param modeTable  Mode Table pointer
/// @param alignment  Binary data alignment [out]
///
/// @return True if successful
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::ReadSections(
    BYTE* const                buffer,
    UINT32 const               bufLength,
    const CHAR* const          fileName,
    ParameterBinaryFile* const file,
    UINT32 const               fileLength,
    UINT32                     pos,
    IModeTable* const          modeTable,
    BYTE const                 alignment)
{
    bool result = ReadSectionInfo(buffer, bufLength, &pos, alignment);

    if (result)
    {

        if ((Flags & FLAGS_COMPRESSED) > 0)
        {
            // Decompress the data
            SetError("Compressed binary file not supported (support ended with V5.1.0)");
            Valid  = false;
            result = false;
        }

        if (result)
        {
            SymbolTable = PARAMETER_NEW ParameterFileSymbolTable(this, Version);
        }

        if (result)
        {
            result = ReadModeSection(buffer, bufLength, file, alignment);
        }

        if (result && SymbolOffset > 0 && SymbolSize > 0 && DataOffset > 0 && DataSize > 0)
        {
            result = SymbolTable->Load(buffer, bufLength, fileName, file, fileLength, SymbolSize,
                SymbolOffset, DataTableSize, DataTableOffset, DataSize, DataOffset, modeTable, alignment);

            if (file != nullptr)
            {
                // Minimize memory usage by freeing the entry data, in delay load/file mode, before loading the module data
                SymbolTable->FreeEntryData();
            }

            if (result && SymbolTable->ModuleCount() > 0)
            {
                UINT32 const modules = SymbolTable->ModuleCount();

                for (UINT32 i = 0; i < modules; i++)
                {
                    ParameterFileSymbolTableEntry entry;
                    ModuleData* const moduleData = SymbolTable->GetModule(i);
                    if (moduleData != nullptr && (!SymbolTable->Find(entry, moduleData->ID) ||
                        !AddModule(moduleData, CreateModule(&entry, moduleData,
                            !IsFileMode() || moduleData->Mode->Preserve))))
                    {
                        Valid  = false;
                        result = false;
                    }
                }
            }

            if (file == nullptr)
            {
                SymbolTable->FreeEntryData();
                SymbolTable->FreeLookupData();
            }
        }
        else if (result)
        {
            PARAMETER_LOG_ERROR(this, "ReadModeSection: Binary missing symbol table or data SymbolOffset(%d) && SymbolSize(%d) && DataOffset(%d) && DataSize(%d)\n",
                SymbolOffset, SymbolSize, DataOffset, DataSize);

        }
    }
    else
    {
        SetError("Invalid binary file section count");
        Valid  = false;
        result = false;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ReadModeSection
///
/// @brief Reads and validates the binary parameter file section table
///
/// @param buffer   Binary parameter buffer
/// @param bufLength  Length of the binary parameter buffer
/// @param file       Binary parameter file pointer
/// @param alignment  Binary data alignment [out]
///
/// @return True if successful
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::ReadModeSection(
    BYTE* const                buffer,
    UINT32                     bufLength,
    ParameterBinaryFile* const file,
    BYTE const                 alignment)
{
    bool   result     = false;
    BYTE* readBuffer = buffer;

    if (ModeSize > 0 && ModeOffset > 0)
    {
        VersionUnion const LinkVersion(LINK_MAJOR, LINK_MINOR, LINK_PATCH);
        VersionUnion const DataTableVersion(DATA_TABLE_MAJOR, DATA_TABLE_MINOR, DATA_TABLE_PATCH);
        VersionUnion const ModeFlagsVersion(MODE_FLAGS_MAJOR, MODE_FLAGS_MINOR, MODE_FLAGS_PATCH);

        // Read Mode Table
        UINT32 const entrySize = SymbolTable->GetModeEntrySize(alignment);
        UINT32 const nameSize  = Version < DataTableVersion ? 0 : MODE_NAME_LEN;

        m_modeCount = (ModeSize - nameSize) / entrySize;

        UINT32 expected = m_modeCount * entrySize + nameSize;
        bool   valid    = ModeSize == expected;

        if (valid)
        {
            UINT32 modePos = ModeOffset;
            m_modes = PARAMETER_NEW ModeEntry*[m_modeCount];
            PARAMETER_MEMSET(m_modes, 0, m_modeCount * sizeof(ModeEntry*));

            if (file != nullptr)
            {
                // Read the mode table from the file
                readBuffer = file->Read(ModeOffset, ModeSize);
                if (readBuffer != nullptr)
                {
                    modePos   = 0;
                    bufLength = ModeSize;
                }
                else
                {
                    PARAMETER_LOG_ERROR(this, "ReadModeSection: Failed to read %d bytes for mode table from binary file\n", ModeSize);
                }
            }

            if (readBuffer == nullptr)
            {
                SetError("ReadModeSection: Failed, buffer is nullptr");
            }
            else if (bufLength == 0)
            {
                SetError("ReadModeSection: Failed, buffer length is 0");
            }
            else
            {
                CHAR* modeName = nullptr;
                if (nameSize > 0)
                {
                    modeName = ParameterUtils::ReadString(readBuffer, bufLength, &modePos, nameSize, alignment);
                    PARAMETER_DELETE[] modeName;
                }

                result = true;

                for (UINT32 i = 0; valid && i < m_modeCount; i++)
                {
                    UINT32    id        = 0;
                    UINT32    parentId  = 0;
                    UINT32    linkId    = NOT_LINKED_ID;
                    UINT32    modeFlags = 0;
                    ModeUnion mode;

                    valid = ParameterUtils::Read(&id,         readBuffer, bufLength, &modePos, alignment, this);
                    if (valid)
                    {
                        valid = ParameterUtils::Read(&mode.Value, readBuffer, bufLength, &modePos, alignment, this);
                    }
                    if (valid)
                    {
                        valid = ParameterUtils::Read(&parentId,   readBuffer, bufLength, &modePos, alignment, this);
                    }

                    // Read link id from Version 2.x binaries
                    if (valid && Version >= LinkVersion)
                    {
                        valid = ParameterUtils::Read(&linkId, readBuffer, bufLength, &modePos, alignment, this);
                    }

                    // Read mode flags from Version 5.x binaries
                    if (valid && Version >= ModeFlagsVersion)
                    {
                        valid = ParameterUtils::Read(&modeFlags, readBuffer, bufLength, &modePos, alignment, this);
                    }

                    if (id >= m_modeCount)
                    {
                        SetError("ReadModeSection: Mode ID exceeds mode count");
                        result = false;
                        continue;
                    }

                    if (parentId >= m_modeCount)
                    {
                        SetError("ReadModeSection: Parent Mode ID exceeds mode count");
                        result = false;
                        continue;
                    }

                    if (linkId != NOT_LINKED_ID && linkId >= m_modeCount)
                    {
                        SetError("ReadModeSection: Link Mode ID exceeds mode count");
                        result = false;
                        continue;
                    }

                    ModeEntry*  parent = nullptr;
                    ModeEntry** link   = nullptr;
                    ModeEntry*  entry  = nullptr;
                    if (parentId != id)
                    {
                        parent = m_modes[parentId];
                    }

                    if (linkId != NOT_LINKED_ID)
                    {
                        link = &m_modes[linkId];
                    }

                    if (parent == nullptr || !parent->Mode.IsModeMatch(mode))
                    {
                        entry = PARAMETER_NEW ModeEntry(!IsFileMode(), (modeFlags & MODE_FLAGS_PRESERVE) > 0);
                        entry->Initialize(id, mode, link);
                    }
                    else
                    {
                        entry = parent;
                    }

                    m_modes[id] = entry;

                    if (m_rootMode == nullptr)
                    {
                        m_rootMode = entry;

                        // Root mode is always loaded and preserved
                        m_rootMode->Loaded   = true;
                        m_rootMode->Preserve = true;
                    }
                    else
                    {
                        if (entry->Mode.Mode.Mode <= MIN_PRESERVE_MODE)
                        {
                            entry->Loaded   = true;
                            entry->Preserve = true;
                        }
                        if (parent != nullptr && parent != entry)
                        {
                            parent->AddMode(entry);
                        }
                    }
                }
            }

            if (readBuffer != buffer)
            {
                PARAMETER_DELETE[] readBuffer;
            }
        }
        else
        {
            PARAMETER_LOG_ERROR(this, "ReadModeSection: ModeSize %d does not match expected size %d\n", ModeSize, expected);
        }
    }
    else
    {
        // Create default Mode Table
        PARAMETER_LOG_DEBUG(this, "ReadModeSection: No mode table in binary, ModeOffset(%d) ModeSize(%d), creating default\n", ModeOffset, ModeSize);
        result      = true;
        m_modeCount = 1;
        m_modes     = PARAMETER_NEW ModeEntry*[m_modeCount];
        m_modes[0]  = PARAMETER_NEW ModeEntry(true, true);
        m_rootMode  = m_modes[0];
        m_rootMode->Initialize(0, 0, 0, nullptr);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ReloadBinaryParameters
///
/// @brief Unloads then Reloads binary parameters from a file
///
/// @param bufferMode     Reload the binary paramaters in buffer mode
///
/// @return True if binary is valid
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::ReloadBinaryParameters(
    bool bufferMode)
{
    bool result = false;
    if (IsFileMode())
    {
        PARAMETER_LOG_DEBUG(this, "Reloading binary file %s%s\n", m_file, bufferMode ? " in buffer mode" : "");

        const ParameterMutexLock rlock {ReloadMutex, false, true}; // Unique LOCK

        FreeReplacedParameters();
        SaveAndClearModules();

        if (bufferMode && SymbolTable->File != nullptr)
        {
            SymbolTable->FileReference(-1);
        }

        m_replacedST.Add(SymbolTable);
        SymbolTable = nullptr;

        FreeBinaryParameters();

        if (bufferMode)
        {
            ParameterBinaryFile* fp = PARAMETER_NEW ParameterBinaryFile(m_file, this);

            if (fp != nullptr)
            {
                if (fp->IsValid())
                {
                    if (fp->IsValidCRC())
                    {
                        PARAMETER_INITIALIZE(m_file);

                        UINT32      length = fp->Length();
                        BYTE* const buffer = fp->Read(0, length);
                        if (buffer != nullptr)
                        {
                            result = LoadBinaryParameters(buffer, length);
                            PARAMETER_DELETE[] buffer;
                        }
                        else
                        {
                            SetError("ReloadBinaryParameters: Failed to allocate space to read data from file");
                        }
                    }
                    else
                    {
                        SetError("ReloadBinaryParameters: File CRC check failed");
                    }
                }
                else
                {
                    SetError("ReloadBinaryParameters: Failed to open file");
                }

                PARAMETER_DELETE fp;
            }
            else
            {
                SetError("ReloadBinaryParameters: Failed to allocate ParameterBinaryFile class");
            }
        }
        else
        {
            result = LoadBinaryParameters(m_file);
        }
    }
    else
    {
        SetError("ReloadBinaryParameters called with empty file name");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// FreeBinaryParameters
///
/// @brief Frees all variables allocated when loading binary parameters
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::FreeBinaryParameters()
{
    PARAMETER_LOG_DEBUG(this, "FreeBinaryParameters called\n");
    PARAMETER_DELETE   m_rootMode;
    PARAMETER_DELETE[] m_modes;
    PARAMETER_DELETE[] BinaryTag;
    PARAMETER_DELETE   SymbolTable;

    m_rootMode    = nullptr;
    m_modes       = nullptr;
    BinaryTag     = nullptr;
    SymbolTable = nullptr;
    m_modeCount   = 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SetError
///
/// @brief Reports an error
///
/// @param message  Binary parameter buffer
/// @param entry    Symbol table entry containing the error
/// @param module   Parameter module containing the error
///
/// @return None
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::SetError(
    const CHAR* message,
    const ModuleData* const moduleData)
{
    if (moduleData != nullptr)
    {
        PARAMETER_SPRINTF(Error, ERROR_LEN, "%s Type: %s, Major: %u, Minor: %u, Patch: %u, Mode: %u",
            message, moduleData->Type, moduleData->Version.Version.Major, moduleData->Version.Version.Minor,
            moduleData->Version.Version.Patch, moduleData->Mode->Mode.Mode.Mode);
    }
    else
    {
        PARAMETER_STRCPY(Error, ERROR_LEN, message);
    }
    PARAMETER_LOG_ERROR(this, "%s\n", Error);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// CreateModule
///
/// @brief Creates a new parameter module from a symbol table entry
///
/// @param entry    Symbol table entry
///
/// @return Parameter module
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ParameterModule* ParameterSetManager::CreateModule(
    const ParameterFileSymbolTableEntry* const entry,
    ModuleData* const moduleData,
    bool const load)
{
    ParameterModule*      result = nullptr;
    DefaultModule* const  module = GetDefaultModule(moduleData->Type);

    if (module != nullptr)
    {
        result = module->Template->Create(moduleData, module);
        if (result == nullptr)
        {
            SetError("Failed to parse binary header data for: ", moduleData);
        }
        else if (load)
        {
            (void)result->LoadModule(entry->Table);
        }
    }
    else if (!SimulatorMode)
    {
        SetError("Failed to find header module: ", moduleData);
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// LoadModules
///
/// @brief Loads or unloads all modules for the specified mode, and children if specified
///
/// @param mode              Mode to load
/// @param load              true - Load all modules, false - Unload all modules
/// @param caller            Name of calling function (for logging purposes)
/// @param callerModeBranch  caller's mode branch (for logging purposes)
/// @param callerModeCount   caller's mode count (for logging purposes)
/// @param fileRefs     Number of file references opened (caller must release them)
///
/// @return true if valid, false if reload is required
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::LoadModules(
    ModeEntry*              mode,
    bool                    load,
    const CHAR*             caller,
    const TuningMode* const callerModeBranch,
    UINT32 const            callerModeCount,
    INT32&                  fileRefs)
{
    bool result = true;
    ModeEntry* inputMode = mode;
    bool delayLoadMode = IsFileMode() && MinimumModes > 0;

    if (mode != nullptr)
    {
        while (mode->Link != nullptr)
        {
            mode = *mode->Link;
        }

        // Do not unload modules if the mode Preserve flag is set
        if (mode != nullptr && (load || !mode->Preserve))
        {
            PList<ModeEntry*> loadedModes;

            const ParameterMutexLock mlock {ModuleMutex, false, IsFileMode()}; // Unique LOCK

            if (load && !mode->Loaded && IsFileMode())
            {
                fileRefs++;
                SymbolTable->FileReference(1);
            }

            if (load && mode->Mode.Mode.Mode > MIN_PRESERVE_MODE && mode->HasLocalModules())
            {
                // Make sure the requested mode is always moved up the active list
                loadedModes.Add(mode);
            }

            if (!load || mode->Loaded != load)
            {
                CHAR modePath[MAX_MODE_PATH]       = "";
                CHAR callerModePath[MAX_MODE_PATH] = "";

                mode->GetModePath(modePath, MAX_MODE_PATH);
                GetPathFromModeBranch(callerModeBranch, callerModeCount, callerModePath, MAX_MODE_PATH);

                if (caller != nullptr)
                {
                    if (inputMode != mode)
                    {
                        CHAR inputPath[MAX_MODE_PATH] = "";
                        inputMode->GetModePath(inputPath, MAX_MODE_PATH);
                        PARAMETER_LOG_DEBUG(this, "%soading modules (GetModule:%s:%s): %s->%s\n",
                            load ? "L" : "Unl", caller, callerModePath, inputPath, modePath);
                    }
                    else
                    {
                        PARAMETER_LOG_DEBUG(this, "%soading modules (GetModule:%s:%s): %s\n",
                            load ? "L" : "Unl", caller, callerModePath, modePath);
                    }
                }
                else
                {
                    if (inputMode != mode)
                    {
                        CHAR inputPath[MAX_MODE_PATH] = "";
                        inputMode->GetModePath(inputPath, MAX_MODE_PATH);
                        PARAMETER_LOG_DEBUG(this, "%soading modules (GetModule:Unknown:%s): %s->%s\n",
                            load ? "L" : "Unl", callerModePath, inputPath, modePath);
                    }
                    else
                    {
                        PARAMETER_LOG_DEBUG(this, "%soading modules (GetModule:Unknown:%s): %s\n",
                            load ? "L" : "Unl", callerModePath, modePath);
                    }
                }

                std::unordered_map<std::string, ModeEntry::ModuleReference>::iterator iter = mode->ModuleMap.begin();
                while (result && iter != mode->ModuleMap.end())
                {
                    ParameterModule* module = iter->second.Module;

                    if (load)
                    {
                        bool loaded = module->LoadModule(SymbolTable);
                        if (!module->Loaded)
                        {
                            // Module failed to load
                            result = false;
                        }
                        if (loaded && IsFileMode() && MinimumModes > 0 && module->Data != nullptr && module->Data->Mode != nullptr)
                        {
                            if (!loadedModes.Contains(module->Data->Mode))
                            {
                                loadedModes.Add(module->Data->Mode);
                            }
                        }
                    }
                    else
                    {
                        if (module->Data != nullptr && module->Data->Mode == mode)
                        {
                            if (module->Loaded && module->GetReferenceCount() > 0)
                            {
                                PARAMETER_LOG_ERROR(this, "Unloading referenced module %s, Mode: %s, Ref count: %u\n", module->Name, module->Data->ModePath, module->GetReferenceCount());
                            }
                            module->UnloadModule();
                        }
                    }
                    iter++;
                }

                mode->Loaded = load && result;
            }

            if (load)
            {
                UINT32 const count = loadedModes.Count();
                for (UINT32 i = 0; i < count; i++)
                {
                    // Move the mode to the front of the list or add if not in the list
                    ModeEntry* const loadedMode = loadedModes[i];

                    if (loadedMode->Mode.Mode.Mode > MIN_PRESERVE_MODE)
                    {
                        INT32 const index = m_activeModes.IndexOf(loadedMode);
                        if (index > 0)
                        {
                            m_activeModes.RemoveAt(index);
                        }
                        if (index != 0)
                        {
                            m_activeModes.Insert(0, loadedMode);
                        }
                    }
                }

                UINT32 max = PARAMETER_MAX(MAX_ACT_PATHS, MinimumModes);
                if (m_activeModes.Count() > max)
                {
                    for (UINT32 pos = m_activeModes.Count() - 1; pos >= max; pos--)
                    {
                        ModeEntry* const active = m_activeModes[pos];
                        if (active != nullptr)
                        {
                            if (!delayLoadMode || active->Preserve)
                            {
                                // Removed preserved modes from the end of the list
                                m_activeModes.RemoveAt(pos);
                            }
                            else if (delayLoadMode && !active->IsActive())
                            {
                                // Unload the mode (Unloading will remove it from the active list)
                                (void)LoadModules(active, false, caller, callerModeBranch, callerModeCount, fileRefs);
                            }
                        }
                    }
                }
            }
            else
            {
                // This code block is entered only when UnloadMode is called
                // Clear all module reference counts
                UINT32 refCnt = mode->GetReferenceCount(nullptr);
                if (refCnt > 0)
                {
                    CHAR modePath[MAX_MODE_PATH] = "";
                    mode->GetModePath(modePath, MAX_MODE_PATH);
                    PARAMETER_LOG_ERROR(this, "Unloading mode with %u unreleased modules: %s\n", refCnt, modePath);
                }
                mode->ReleaseReferences();
                m_activeModes.Remove(mode);
            }
        }
    }
    else if (mode == nullptr)
    {
        PARAMETER_LOG_ERROR(this, "LoadModules: mode is nullptr\n");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetModeBranchFromPath
///
/// @brief Gets the mode branch, in list format, from a mode path
///
/// @param modePath     Input mode path
/// @param modeBranch   Output mode branch list
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ParameterSetManager::GetModeBranchFromPath(
    const CHAR* const modePath,
    PList<TuningMode>& modeList)
{
    bool result = false;

    if (modePath != nullptr)
    {
        constexpr SIZE_T MAX_SUBSTRING = 10;

        CHAR         subString[MAX_SUBSTRING];
        UINT32       start   = 0;
        UINT32 const modeLen = (UINT32)PARAMETER_STRLEN(modePath);
        UINT32       length;

        result = true;
        do
        {
            length = ParameterUtils::GetSubString(&modePath[start], '|', subString, MAX_SUBSTRING);

            if (length > 0)
            {
                UINT32 mode        = 0;
                UINT16 subMode     = 0;
                UINT32 dotPos      = 0;
                UINT32 modeMult    = 1;
                UINT32 subModeMult = 1;
                for (UINT32 pos = 0; result && pos < length; pos++)
                {
                    if (subString[pos] == '.')
                    {
                        dotPos = pos;
                    }
                    else
                    {
                        if (dotPos == 0 && pos > 0)
                        {
                            modeMult *= 10;
                        }
                        else if (dotPos > 0 && pos > dotPos + 1)
                        {
                            subModeMult *= 10;
                        }
                        result = subString[pos] >= '0' && subString[pos] <= '9';
                    }
                }

                if (result)
                {
                    for (UINT32 pos = 0; pos < dotPos; pos++)
                    {
                        mode += (UINT32)((subString[pos] - '0') * modeMult);
                        modeMult /= 10;
                    }

                    for (UINT32 pos = dotPos + 1; pos < length; pos++)
                    {
                        subMode += (UINT16)((subString[pos] - '0') * subModeMult);
                        subModeMult /= 10;
                    }

                    TuningMode tMode;
                    PARAMETER_INITIALIZE(tMode);

                    tMode.mode = (ModeType)mode;
                    tMode.subMode.value = subMode;
                    modeList.Add(tMode);
                }

                start += length + 1;
            }
        } while (start < modeLen && length != 0 && result);

    }

    return result && modeList.Count() > 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetPathFromModeBranch
///
/// @brief Gets the path from the mode branch
///
/// @param modeBranch   Mode branch
/// @param modeCount    Mode count
/// @param path         Ouptut mode path
/// @param pathSize     Size of path character array, recommended min size is MIN_PATH_SIZE, assuming 7 modes per branch
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterSetManager::GetPathFromModeBranch(
    const TuningMode* const modeBranch,
    UINT32 const            modeCount,
    CHAR* const             path,
    UINT32 const            pathSize)
{
    if (modeBranch != nullptr && modeCount > 0)
    {
        constexpr UINT32 MAX_MODE = 12U; // "65535.65535" + nul terminator
        CHAR mode[MAX_MODE];

        for (UINT32 i = 0; i < modeCount; i++)
        {
            if (i > 0)
            {
                PARAMETER_STRCAT(path, pathSize, "|");
            }
            PARAMETER_SPRINTF(mode, MAX_MODE, "%u.%u", (UINT16)modeBranch[i].mode, modeBranch[i].subMode.value);
            PARAMETER_STRCAT(path, pathSize, mode);
        }
    }
}

UINT16 ParameterSetManager::GetModeIndex(
    const CHAR* mode)
{
    ModeEntry entry(true, true);
    entry.Mode.Mode.Mode = 0;

    if (mode != nullptr)
    {
        SIZE_T      modeLen = PARAMETER_STRLEN(mode);
        const CHAR* modeStr = nullptr;
        do
        {
            modeStr = entry.GetModeName();
            if (modeStr == nullptr)
            {
                // Entry not found, set mode to 0 and exit
                entry.Mode.Mode.Mode = 0;
                break;
            }
            else if (modeLen == PARAMETER_STRLEN(modeStr) && PARAMETER_STRNICMP(mode, modeStr, modeLen) == 0)
            {
                // Match found, exit
                break;
            }
            else
            {
                // Match not found, increment and try again
                entry.Mode.Mode.Mode++;
            }
        } while (modeStr != nullptr);
    }

    return entry.Mode.Mode.Mode;
}

UINT16 ParameterSetManager::GetSubModeIndex(
    UINT16      mode,
    const CHAR* subMode)
{
    ModeEntry entry(true, true);
    entry.Mode.Mode.Mode    = mode;
    entry.Mode.Mode.SubMode = 0;

    if (subMode != nullptr)
    {
        bool isInteger = false;
        const double value = ParameterUtils::StringToDouble(subMode, isInteger);

        if (isInteger)
        {
            entry.Mode.Mode.SubMode = (UINT16)value;
        }
        else
        {
            SIZE_T      subModeLen = PARAMETER_STRLEN(subMode);
            const CHAR* subModeStr = nullptr;
            do
            {
                subModeStr = entry.GetSubModeName();
                if (subModeStr == nullptr)
                {
                    // Entry not found, set mode to 0 and exit
                    entry.Mode.Mode.SubMode = 0;
                    break;
                }
                else if (subModeLen == PARAMETER_STRLEN(subModeStr) && PARAMETER_STRNICMP(subMode, subModeStr, subModeLen) == 0)
                {
                    // Match found, exit
                    break;
                }
                else
                {
                    // Match not found, increment and try again
                    entry.Mode.Mode.SubMode++;
                }
            } while (subModeStr != nullptr);
        }
    }

    return entry.Mode.Mode.SubMode;
}
