/*
 * Copyright © 2006-2023 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 "DynamicModel.hh"
#include "ExtendedPreprocessorTypes.hh"
#include "ExternalFunctionsTable.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);
  //! 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;
  //! Expressions outside model block
  DataTree expressions_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;

  //! 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;

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;
  /* Generate a random temporary path, in the current directory. Equivalent to
     boost::filesystem::unique_path(). Both are insecure, but currently there
     is no better portable solution. Maybe in a later C++ standard? */
  static filesystem::path unique_path();

  /* Hack for removing a directory that is locked by MATLAB/Windows. The
     directory is renamed before being deleted. The renaming must occur in the
     same directory, otherwise it may fail if the destination is not on the
     same filesystem. This technique is applied recursively to subdirectories.
     Works even if the argument does not exist or is not a directory. */
  static void remove_directory_with_matlab_lock(const filesystem::path& dir);

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 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
  void writeJsonOutput(const string& basename, JsonOutputPointType json,
                       JsonFileOutputType json_output_mode, bool onlyjson,
                       bool jsonderivsimple = false);
};

#endif
