/*
 * Copyright © 2006-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/>.
 */

#ifndef MOD_FILE_HH
#define MOD_FILE_HH

#include <ctime>
#include <filesystem>
#include <iostream>
#include <ostream>
#include <sstream>

#include "Configuration.hh"
#include "DatabaseTable.hh"
#include "DynamicModel.hh"
#include "ExtendedPreprocessorTypes.hh"
#include "ExternalFunctionsTable.hh"
#include "HeterogeneityTable.hh"
#include "HeterogeneousModel.hh"
#include "ModelEquationBlock.hh"
#include "NumericalConstants.hh"
#include "NumericalInitialization.hh"
#include "Statement.hh"
#include "StaticModel.hh"
#include "SubModel.hh"
#include "SymbolTable.hh"
#include "WarningConsolidation.hh"

using namespace std;

//! The abstract representation of a "mod" file
class ModFile
{
public:
  explicit ModFile(WarningConsolidation& warnings_arg);
  // For heterogeneity dimensions
  HeterogeneityTable heterogeneity_table;
  //! Symbol table
  SymbolTable symbol_table;
  //! External Functions table
  ExternalFunctionsTable external_functions_table;
  //! Numerical constants table
  NumericalConstants num_constants;
  //! Var Model Table used for storing info about VAR models
  VarModelTable var_model_table;
  //! Trend Component Model Table used for storing info about trend component models
  TrendComponentModelTable trend_component_model_table;
  //! Table for storing the models declared with var_expectation_model
  VarExpectationModelTable var_expectation_model_table;
  //! PAC Model Table used for storing info about pac models
  PacModelTable pac_model_table;
  // For databases (as e.g. used in “shock_paths” block)
  DatabaseTable database_table;
  /* Expressions outside:
     + model, steady_state_model, epilogue and shock_paths blocks
     + planner_objective statement
     + trends in var statements */
  DataTree expressions_tree;
  // Expressions inside shock_paths blocks
  DataTree shock_paths_tree;
  //! Original model, as declared in the "model" block, that won't be modified by the preprocessor
  DynamicModel original_model;
  //! Dynamic model, as declared in the "model" block
  DynamicModel dynamic_model;
  //! A copy of Dynamic model, for testing trends declared by user
  DynamicModel trend_dynamic_model;
  //! A copy of the original model, used to test model linearity under ramsey problem
  OrigRamseyDynamicModel orig_ramsey_dynamic_model;
  //! Epilogue model, as declared in the "epilogue" block
  Epilogue epilogue;
  //! Static model, as derived from the "model" block when leads and lags have been removed
  StaticModel static_model;
  //! Static model, as declared in the "steady_state_model" block if present
  SteadyStateModel steady_state_model;
  // Heterogeneous model blocks, ordered per heterogeneity dimension ID
  vector<HeterogeneousModel> heterogeneous_models;

  //! Option linear
  bool linear {false};

  //! Was the “block” option passed to the “model” block?
  bool block {false};

  //! Is the model stored in bytecode format (bytecode=true) or in a M-file (bytecode=false)
  bool bytecode {false};

  /*! Is the model stored in a MEX file ? (option “use_dll”, either in the
    “model” block or on the preprocessor command line) */
  bool use_dll {false};

  //! Is the static model have to computed (no_static=false) or not (no_static=true). Option of
  //! 'model'
  bool no_static {false};

  //! Is the 'differentiate_forward_vars' option used?
  bool differentiate_forward_vars {false};

  /*! If the 'differentiate_forward_vars' option is used, contains the set of
    endogenous with respect to which to do the transformation;
    if empty, means that the transformation must be applied to all endos
    with a lead */
  vector<string> differentiate_forward_vars_subset;

  //! Are nonstationary variables present ?
  bool nonstationary_variables {false};

  //! Global evaluation context
  /*! Filled using initval blocks and parameters initializations */
  eval_context_t global_eval_context;

  //! Parameter used with lead/lag
  bool param_used_with_lead_lag {false};

  //! Stores the list of extra files to be transefered during a parallel run
  /*! (i.e. option parallel_local_files of model block) */
  vector<string> parallel_local_files;

  /* Contents of the ramsey_constraints block.
     Maps symb_id → (lower_bound, upper_bound).
     NB: The two expr_t live in dynamic_model */
  map<int, pair<expr_t, expr_t>> ramsey_constraints;

private:
  //! List of statements
  vector<unique_ptr<Statement>> statements;
  //! Structure of the mod file
  ModFileStructure mod_file_struct;
  //! Warnings Encountered
  WarningConsolidation& warnings;
  //! Functions used in writing of JSON outut. See writeJsonOutput
  void writeJsonOutputParsingCheck(const string& basename, JsonFileOutputType json_output_mode,
                                   bool transformpass, bool computingpass) const;
  void writeJsonComputingPassOutput(const string& basename, JsonFileOutputType json_output_mode,
                                    bool jsonderivsimple) const;
  void writeJsonFileHelper(const filesystem::path& fname, ostringstream& output) const;

public:
  //! Add a statement
  void addStatement(unique_ptr<Statement> st);
  //! Add a statement at the front of the statements vector
  void addStatementAtFront(unique_ptr<Statement> st);
  //! Evaluate all the statements
  /*! \param warn_uninit Should a warning be displayed for uninitialized
   * endogenous/exogenous/parameters ? */
  void evalAllExpressions(bool warn_uninit);
  //! Do some checking and fills mod_file_struct
  /*! \todo add check for number of equations and endogenous if ramsey_policy is present */
  void checkPass(bool nostrict, bool stochastic);
  //! Perform some transformations on the model (creation of auxiliary vars and equations)
  /*! \param compute_xrefs if true, equation cross references will be computed */
  void transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool transform_unary_ops,
                     const string& exclude_eqs, const string& include_eqs);
  //! Execute computations
  /*! \param no_tmp_terms if true, no temporary terms will be computed in the static and dynamic
   * files */
  /*! \param params_derivs_order compute this order of derivs wrt parameters */
  void computingPass(bool no_tmp_terms, OutputType output, int params_derivs_order);
  //! Writes Matlab/Octave output files
  /*!
    \param basename The base name used for writing output files. Should be the name of the mod file
    without its extension \param clear_all Should a "clear all" instruction be written to output ?
    \param console Are we in console mode ?
    \param nograph Should we build the figures?
    \param nointeractive Should Dynare request user input?
    \param cygwin Should the MEX command of use_dll be adapted for Cygwin?
    \param msvc Should the MEX command of use_dll be adapted for MSVC?
    \param mingw Should the MEX command of use_dll be adapted for MinGW?
    \param compute_xrefs if true, equation cross references will be computed
  */
  void writeMOutput(const string& basename, bool clear_all, bool clear_global, bool no_warn,
                    bool console, bool nograph, bool nointeractive, const Configuration& config,
                    bool check_model_changes, bool minimal_workspace, bool compute_xrefs,
                    const string& mexext, const filesystem::path& matlabroot, bool onlymodel,
                    bool gui, bool notime) const;

  void writeJuliaOutput(const string& basename) const;

  void writePythonOutput(const string& basename, bool use_jax, bool use_numba) const;

  void writeJsonOutput(const string& basename, JsonOutputPointType json,
                       JsonFileOutputType json_output_mode, bool onlyjson,
                       bool jsonderivsimple = false);

private:
  void writePythonDriverFile(const string& basename, bool use_jax, bool use_numba) const;
  void writePythonInitFile(const filesystem::path& python_dir) const;
  void writePythonModelInterfaceFile(const filesystem::path& python_dir, bool use_jax,
                                     bool use_numba) const;
  void writePythonTypeStubs(const string& basename) const;

  void computeChecksum();
  //! Write JSON representation of ModFile object
  //! Initially created to enable Julia to work with .mod files
  //! Potentially outputs ModFile after the various parts of processing (parsing, checkPass,
  //! transformPass, computingPass) Allows user of other host language platforms (python, fortran,
  //! etc) to provide support for dynare .mod files
};

#endif
