﻿/**
@file ExpandVars.cpp
@brief ExpandVars function

This source file contains the definition of the variation of the ExpandVars function that depends
on nothing beyond the STL.
*/
#include "yekneb_config.h"

#include <map>
#include <string>

#include "ExpandVars.h"
#include "detail/ExpandVarsDetail.h"

#include "ConstantOfType.h"

namespace yekneb {

/**
@brief Returns a string in which the variables are expanded based on the values specified in either
an environment map or the environment variables.

This function returns a string in which the variables are expanded based on the values specified in
either an environment map or the environment variables.

The following formats are supported for variable names.
• %VarName% (Win32)
• %(VarName)
• %[VarName]
• %{VarName}
• $(VarName)
• $[VarName]
• ${VarName}
• #(VarName)
• #[VarName]
• #{VarName}

Bash style variable names in the form of $VarName are not supported.

This implementation uses nothing beyond the STL.

@param[in] original
    A string containing variable specifications in the form of %VarName%, %(VarName), %[VarName],
    %{VarName}, $(VarName), $[VarName], ${VarName}, #(VarName), #[VarName], or #{VarName} that are
    to be expanded.

@param[in] env
    A string map that maps variable names to their replacement values.

@return
    A string in which the variable specifications have been expanded using either the specified
    environment map or the system environment variables.
*/
template<typename CharType>
std::basic_string<CharType> ExpandVars(const std::basic_string<CharType>& original,
    const std::map<std::basic_string<CharType>, std::basic_string<CharType>>& env)
{
    using string_type = typename std::basic_string<CharType>;
    using size_type = typename std::basic_string<CharType>::size_type;
    string_type ret{original};
    if (original.empty())
    {
        return {};
    }
    bool foundVar{false};
    size_type pos{0};
    // NOLINTNEXTLINE(cppcoreguidelines-avoid-do-while)
    do
    {
        size_type beginVarStringPos{0};
        size_type endVarStringPos{0};
        size_type beginVarNamePos{0};
        size_type endVarNamePos{0};
        foundVar = ::yekneb::detail::ExpandVars::FindVariableString(
            ret, pos, beginVarStringPos, endVarStringPos,
            beginVarNamePos, endVarNamePos);
        if (foundVar)
        {
            const size_type varStringLen{endVarStringPos - beginVarStringPos + 1};
            [[maybe_unused]] const string_type varString = ret.substr(beginVarStringPos, varStringLen);
            const size_type varNameLen{endVarNamePos - beginVarNamePos + 1};
            const string_type varName{ret.substr(beginVarNamePos, varNameLen)};
            bool fromEnvMap{false};
            bool valueContainsVariableStrings{false};
            const string_type value{::yekneb::detail::ExpandVars::GetVariableValue(varName, env, fromEnvMap,
				valueContainsVariableStrings)};
            if (!value.empty())
            {
                ret = ret.replace(beginVarStringPos, varStringLen, value);
                pos = beginVarStringPos;
            }
            else
            {
                pos = endVarStringPos + 1;
            }
        }
    } while (foundVar);
    return ret;
}

template std::basic_string<char> ExpandVars<char>(const std::basic_string<char>& original,
    const std::map<std::basic_string<char>, std::basic_string<char>>& env);
template std::basic_string<wchar_t> ExpandVars<wchar_t>(const std::basic_string<wchar_t>& original,
    const std::map<std::basic_string<wchar_t>, std::basic_string<wchar_t>>& env);

} // namespace yekneb
