/****************************************************************************
 *   This file is part of the aGrUM/pyAgrum library.                        *
 *                                                                          *
 *   Copyright (c) 2005-2026 by                                             *
 *       - Pierre-Henri WUILLEMIN(_at_LIP6)                                 *
 *       - Christophe GONZALES(_at_AMU)                                     *
 *                                                                          *
 *   The aGrUM/pyAgrum library is free software; you can redistribute it    *
 *   and/or modify it under the terms of either :                           *
 *                                                                          *
 *    - the GNU Lesser General Public License as published by               *
 *      the Free Software Foundation, either version 3 of the License,      *
 *      or (at your option) any later version,                              *
 *    - the MIT license (MIT),                                              *
 *    - or both in dual license, as here.                                   *
 *                                                                          *
 *   (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html)    *
 *                                                                          *
 *   This aGrUM/pyAgrum library is distributed in the hope that it will be  *
 *   useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,          *
 *   INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
 *   FOR A PARTICULAR PURPOSE  AND NONINFRINGEMENT. IN NO EVENT SHALL THE   *
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,        *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR  *
 *   OTHER DEALINGS IN THE SOFTWARE.                                        *
 *                                                                          *
 *   See LICENCES for more details.                                         *
 *                                                                          *
 *   SPDX-FileCopyrightText: Copyright 2005-2026                            *
 *       - Pierre-Henri WUILLEMIN(_at_LIP6)                                 *
 *       - Christophe GONZALES(_at_AMU)                                     *
 *   SPDX-License-Identifier: LGPL-3.0-or-later OR MIT                      *
 *                                                                          *
 *   Contact  : info_at_agrum_dot_org                                       *
 *   homepage : http://agrum.gitlab.io                                      *
 *   gitlab   : https://gitlab.com/agrumery/agrum                           *
 *                                                                          *
 ****************************************************************************/


/**
 * @file
 * @brief Headers of O3prmInterpreter
 *
 * @author Pierre-Henri WUILLEMIN(_at_LIP6), Ni NI, Lionel TORTI & Vincent RENAUDINEAU
 */

#ifndef GUM_O3PRMR_INTERPRETER_H
#define GUM_O3PRMR_INTERPRETER_H

#ifndef DOXYGEN_SHOULD_SKIP_THIS
#  include <agrum/PRM/o3prm/O3prmReader.h>
#  include <agrum/PRM/o3prmr/O3prmrContext.h>

#endif   // DOXYGEN_SHOULD_SKIP_THIS

namespace gum {

  namespace prm {

    namespace o3prmr {

      /**
       *
       * */
      class SingleResult {
        public:
        std::string label;
        float       p;
      };

      /**
       *
       * */
      class QueryResult {
        public:
        double                      time;
        std::string                 command;
        std::vector< SingleResult > values;
      };

      /**
       * @brief Represents a O3PRMR context.
       * O3prmr use the @<double@> version of the O3PRM templatized ecosystem
       */
      class O3prmrInterpreter {
        public:
        /// This constructor create an empty context.
        O3prmrInterpreter();
        /// Destructor. Delete current context.
        ~O3prmrInterpreter();

        /**
         * Interpret the file or the command line.
         * If syntax mode is activated, check only syntax and semantic.
         * If errors occured, return false. Errors messages can be retrieve be
         * errorsContainer() methods.
         * If any errors occured, return true.
         * Requests results can be retrieve be results() methods.
         * */
        bool interpretFile(std::string_view filename);
        bool interpretLine(std::string_view line);

        /// Retrieve prm object.
        const gum::prm::PRM< double >* prm() const;
        /// Retrieve inference motor object.
        const gum::prm::PRMInference< double >* inference() const;
        /// Return a vector of QueryResults.
        /// Each QueryResults is a struct with query command, time and values,
        /// a vector of struct SingleResult, with pair label/value.
        const std::vector< QueryResult >& results() const;
        /// Return container with all errors.
        ErrorsContainer errorsContainer() const;

        /// Getter and setter for the context.
        O3prmrContext< double >* getContext() const;
        void                     setContext(O3prmrContext< double >* context);

        /// Root paths to search from there packages.
        /// Default are working dir, request file dir if any
        /// and one is calculate from package if any.
        std::vector< std::string > getPaths() const;
        void                       addPath(std::string path);
        void                       clearPaths();

        /// syntax mode don't process anything, just check syntax. Default is
        /// false.
        bool isInSyntaxMode() const;
        void setSyntaxMode(bool f);

        /// verbose mode show more details on the program execution. Default is
        /// false.
        bool isVerboseMode() const;
        void setVerboseMode(bool f);

        /**
         * En cas d'échec, l'API de gestion d'erreurs est présente.
         * */
        /// # of errors + warnings
        Size count() const;
        /// # of errors
        Size errors() const;
        /// # of warnings
        Size warnings() const;
        /// throw a string error if i >= count
        ParseError error(Idx i) const;
        /// send on std::cerr the list of errors
        void showElegantErrors(std::ostream& o = std::cerr) const;
        /// send on std::cerr the list of errors or warnings
        void showElegantErrorsAndWarnings(std::ostream& o = std::cerr) const;
        /// send on std::cerr the number of errors and the number of warnings
        void showErrorCounts(std::ostream& o = std::cerr) const;

        private:
        bool checkSemantic(O3prmrContext< double >* context);
        bool checkSetEngine(SetEngineCommand* command);
        bool checkSetGndEngine(SetGndEngineCommand* command);
        bool checkObserve(ObserveCommand< double >* command);
        bool checkUnobserve(UnobserveCommand< double >* command);
        bool checkQuery(QueryCommand< double >* command);

        bool interpret(O3prmrContext< double >* c);
        bool import(O3prmrContext< double >* context, std::string import);
        bool observe(const ObserveCommand< double >* command);
        bool unobserve(const UnobserveCommand< double >* command);
        void query(const QueryCommand< double >* command);
        void setEngine(const SetEngineCommand* command);
        void setGndEngine(const SetGndEngineCommand* command);

        std::string findSystemName(std::string& s);
        std::string findInstanceName(std::string& s, const gum::prm::PRMSystem< double >& sys);
        std::string findAttributeName(std::string_view                       s,
                                      const gum::prm::PRMInstance< double >& instance);
        const PRMSystem< double >& system(std::string& ident);
        void                       generateInfEngine(const gum::prm::PRMSystem< double >& sys);

        void addError(std::string msg);
        void addWarning(std::string msg);

        std::string _readFile_(std::string_view file);

        O3prmrContext< double >*                m_context;
        gum::prm::o3prm::O3prmReader< double >* m_reader;
        std::vector< std::string >              m_paths;
        gum::BayesNet< double >*                m_bn;
        // Don't delete this, the m_inf_map will do it
        gum::prm::PRMInference< double >*                                m_inf;
        HashTable< const PRMSystem< double >*, PRMInference< double >* > m_inf_map;
        std::string                                                      m_engine;
        std::string                                                      m_bn_engine;
        std::vector< QueryResult >                                       m_results;
        gum::ErrorsContainer                                             m_errors;
        bool                                                             m_syntax_flag;
        bool                                                             m_verbose;
        std::ostream&                                                    m_log;
        int                                                              m_current_line;

        //// We need this when using grounded inference
        // gum::BayesNetInference<gum::prm::prm_float>* bn;

      };   // class O3prmrInterpreter

    }   // namespace o3prmr
  }   // namespace prm
}   // namespace gum

#endif   // GUM_GUM_O3PRMR_INTERPRETER_H
