/****************************************************************************
** Copyright (c) 2016, Fougue SAS <https://www.fougue.pro>
** SPDX-License-Identifier: BSD-2-Clause
****************************************************************************/

#include "occ_static_variables_rollback.h"

#include <Interface_Static.hxx>
#include <fmt/format.h>
#include <iostream>
#include <type_traits>

namespace Mayo::IO {

struct OccStaticVariablesRollback::Private {
    template<typename T>
    static StaticVariableRecord createStaticVariableRecord(const char* strKey)
    {
        if (!Interface_Static::IsPresent(strKey)) {
            std::cerr << fmt::format("OpenCascade static variable doesn't exist [varname={}]", strKey) << std::endl;
            return {};
        }

        StaticVariableRecord record;
        record.strKey = strKey;
        if constexpr(std::is_same_v<int, T>) {
            record.value = Interface_Static::IVal(strKey);
        }
        else if constexpr(std::is_same_v<double, T>) {
            record.value = Interface_Static::RVal(strKey);
        }
        else if constexpr(std::is_same_v<std::string_view, T>) {
            record.value = Interface_Static::CVal(strKey);
        }

        return record;
    }

    template<typename T>
    static bool changeStaticVariable(const char* strKey, T value)
    {
        bool ok = false;
        if constexpr(std::is_same_v<int, T>) {
            ok = Interface_Static::SetIVal(strKey, value);
        }
        else if constexpr(std::is_same_v<double, T>) {
            ok = Interface_Static::SetRVal(strKey, value);
        }
        else if constexpr(std::is_same_v<std::string_view, T>) {
            ok = Interface_Static::SetCVal(strKey, value.data());
        }
        else if constexpr(std::is_same_v<std::string, T>) {
            ok = Interface_Static::SetCVal(strKey, value.c_str());
        }

        if (!ok)
            std::cerr << fmt::format("Failed to change OpenCascade static variable [varname={}]", strKey) << std::endl;

        return ok;
    }
};

bool OccStaticVariablesRollback::StaticVariableRecord::isValid() const
{
    return !this->strKey.empty() && !this->value.valueless_by_exception();
}

void OccStaticVariablesRollback::change(const char* strKey, int newValue)
{
    const auto record = Private::createStaticVariableRecord<int>(strKey);
    Private::changeStaticVariable(strKey, newValue);
    if (record.isValid())
        m_vecRecord.push_back(std::move(record));
}

void OccStaticVariablesRollback::change(const char* strKey, double newValue)
{
    const auto record = Private::createStaticVariableRecord<double>(strKey);
    Private::changeStaticVariable(strKey, newValue);
    if (record.isValid())
        m_vecRecord.push_back(std::move(record));
}

void OccStaticVariablesRollback::change(const char* strKey, std::string_view newValue)
{
    const auto record = Private::createStaticVariableRecord<std::string_view>(strKey);
    Private::changeStaticVariable(strKey, newValue);
    if (record.isValid())
        m_vecRecord.push_back(std::move(record));
}

OccStaticVariablesRollback::~OccStaticVariablesRollback()
{
    for (const StaticVariableRecord& record : m_vecRecord) {
        if (std::holds_alternative<int>(record.value))
            Private::changeStaticVariable(record.strKey.c_str(), std::get<int>(record.value));
        else if (std::holds_alternative<double>(record.value))
            Private::changeStaticVariable(record.strKey.c_str(), std::get<double>(record.value));
        else if (std::holds_alternative<std::string>(record.value))
            Private::changeStaticVariable(record.strKey.c_str(), std::get<std::string>(record.value));
    }
}

} // namespace Mayo::IO
