/*
 * Copyright © 2003-2026 Dynare Team
 *
 * This file is part of Dynare.
 *
 * Dynare is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Dynare is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Dynare.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <ranges>
#include <regex>

#include "SymbolList.hh"

SymbolList::SymbolList(vector<string> symbols_arg) : symbols {move(symbols_arg)}
{
}

void
SymbolList::checkPass(WarningConsolidation& warnings, const vector<SymbolType>& types,
                      const SymbolTable& symbol_table) const noexcept(false)
{
  if (types.empty())
    return;

  smatch m;
  string regex_str = "AUX_EXPECT_|MULT_";
  for (auto type : types)
    if (type == SymbolType::endogenous)
      {
        regex_str += "|AUX_ENDO_|LOG_";
        break;
      }
  regex re("^(" + regex_str + ")");
  for (const auto& symbol : symbols)
    {
      if (!symbol_table.exists(symbol))
        {
          if (regex_search(symbol, m, re))
            {
              warnings
                  << "WARNING: symbol_list variable " << symbol << " has not yet been declared. "
                  << "This is being ignored because the variable name corresponds to a possible "
                  << "auxiliary variable name." << '\n';
              return;
            }
          else
            throw SymbolListException {"Variable " + symbol + " was not declared."};
        }

      if (ranges::none_of(types,
                          [&](SymbolType type) { return symbol_table.getType(symbol) == type; }))
        {
          string msg {"Variable " + symbol + " is not one of {"};
          for (bool printed_something {false}; auto type : types)
            {
              if (exchange(printed_something, true))
                msg += ", ";
              msg += to_string(type);
            }
          msg += "}";
          throw SymbolListException {msg};
        }
    }
}

void
SymbolList::writeOutput(const string& varname, ostream& output) const
{
  output << varname << " = {";
  for (bool printed_something {false}; const auto& name : symbols)
    {
      if (exchange(printed_something, true))
        output << ";";
      output << "'" << name << "'";
    }
  output << "};" << '\n';
}

void
SymbolList::writeJsonOutput(ostream& output) const
{
  output << R"("symbol_list": [)";
  for (bool printed_something {false}; const auto& name : symbols)
    {
      if (exchange(printed_something, true))
        output << ",";
      output << R"(")" << name << R"(")";
    }
  output << "]";
}

vector<string>
SymbolList::getSymbols() const
{
  return symbols;
}

void
SymbolList::removeDuplicates(const string& dynare_command, WarningConsolidation& warnings)
{
  vector<string> unique_symbols;
  for (const auto& it : symbols)
    if (ranges::find(unique_symbols, it) == unique_symbols.end())
      unique_symbols.push_back(it);
    else
      warnings << "WARNING: In " << dynare_command << ": " << it
               << " found more than once in symbol list. Removing all but first occurrence."
               << '\n';
  symbols = unique_symbols;
}
