/****************************************************************************
 *   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                           *
 *                                                                          *
 ****************************************************************************/

#pragma once


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

#include <agrum/PRM/o3prmr/O3prmrContext.h>   // to ease IDE parser

#include "O3prmrContext.h"

namespace gum {
  namespace prm {
    namespace o3prmr {

      /* ******************************************************************* */

      template < GUM_Numeric GUM_SCALAR >
      ObserveCommand< GUM_SCALAR >::ObserveCommand(int              line,
                                                   std::string_view leftValue,
                                                   std::string_view rightValue) :
          O3prmrCommand(line), leftValue(leftValue), rightValue(rightValue), system(0) {}

      template < GUM_Numeric GUM_SCALAR >
      ObserveCommand< GUM_SCALAR >::ObserveCommand(const ObserveCommand& c) :
          O3prmrCommand(c), leftValue(c.leftValue), rightValue(c.rightValue), system(c.system),
          chain(c.chain) {}

      template < GUM_Numeric GUM_SCALAR >
      O3prmrCommand::RequestType ObserveCommand< GUM_SCALAR >::type() const {
        return RequestType::Observe;
      }

      template < GUM_Numeric GUM_SCALAR >
      std::string ObserveCommand< GUM_SCALAR >::toString() const {
        return leftValue + " = " + rightValue + ";";
      }

      /* ******************************************************************* */

      template < GUM_Numeric GUM_SCALAR >
      UnobserveCommand< GUM_SCALAR >::UnobserveCommand(int line, std::string_view value) :
          O3prmrCommand(line), value(value), system(0) {}

      template < GUM_Numeric GUM_SCALAR >
      UnobserveCommand< GUM_SCALAR >::UnobserveCommand(const UnobserveCommand& c) :
          O3prmrCommand(c), value(c.value), system(c.system), chain(c.chain) {}

      template < GUM_Numeric GUM_SCALAR >
      O3prmrCommand::RequestType UnobserveCommand< GUM_SCALAR >::type() const {
        return RequestType::Unobserve;
      }

      template < GUM_Numeric GUM_SCALAR >
      std::string UnobserveCommand< GUM_SCALAR >::toString() const {
        return "unobserve " + value + ";";
      }

      /* ******************************************************************* */

      template < GUM_Numeric GUM_SCALAR >
      QueryCommand< GUM_SCALAR >::QueryCommand(int line, std::string_view val) :
          O3prmrCommand(line), value(val), system(nullptr) {}

      template < GUM_Numeric GUM_SCALAR >
      O3prmrCommand::RequestType QueryCommand< GUM_SCALAR >::type() const {
        return RequestType::Query;
      }

      template < GUM_Numeric GUM_SCALAR >
      std::string QueryCommand< GUM_SCALAR >::toString() const {
        return "? " + value + ";";
      }

      /* ******************************************************************* */

      template < GUM_Numeric GUM_SCALAR >
      const ImportCommand* O3prmrContext< GUM_SCALAR >::mainImport() const {
        return m_mainImport;
      }

      template < GUM_Numeric GUM_SCALAR >
      void O3prmrContext< GUM_SCALAR >::addImport(const ImportCommand& i) {
        m_imports.push_back(new ImportCommand(i.line, i.value, i.alias));

        if (i.alias == "default") m_mainImport = m_imports.back();
      }

      /* ******************************************************************* */

      template < GUM_Numeric GUM_SCALAR >
      O3prmrContext< GUM_SCALAR >::O3prmrContext(std::string_view filename) {
        m_filename   = filename;
        m_mainImport = 0;
      }

      template < GUM_Numeric GUM_SCALAR >
      O3prmrContext< GUM_SCALAR >::O3prmrContext(const O3prmrContext& s) {
        m_filename   = s.m_filename;
        m_mainImport = s.m_mainImport;
        *this += s;
      }

      template < GUM_Numeric GUM_SCALAR >
      O3prmrContext< GUM_SCALAR >::~O3prmrContext() {
        for (Idx i = Size(m_imports.size()); i > 0; i--)
          delete m_imports[i - 1];

        for (Size i = Size(m_sessions.size()); i > 0; i--)
          delete m_sessions[i - 1];
      }

      template < GUM_Numeric GUM_SCALAR >
      std::string O3prmrContext< GUM_SCALAR >::filename() const {
        return m_filename;
      }

      template < GUM_Numeric GUM_SCALAR >
      std::string O3prmrContext< GUM_SCALAR >::package() const {
        return m_package;
      }

      template < GUM_Numeric GUM_SCALAR >
      void O3prmrContext< GUM_SCALAR >::setPackage(std::string_view package) {
        m_package = package;
      }

      template < GUM_Numeric GUM_SCALAR >
      std::string O3prmrContext< GUM_SCALAR >::aliasToImport(std::string_view alias) {
        for (Idx i = Size(m_imports.size()); i > 0; i--)
          if (m_imports[i - 1]->alias == alias) return m_imports[i - 1]->value;

        return {};
      }

      template < GUM_Numeric GUM_SCALAR >
      std::vector< ImportCommand* > O3prmrContext< GUM_SCALAR >::imports() const {
        return m_imports;
      }

      template < GUM_Numeric GUM_SCALAR >
      void O3prmrContext< GUM_SCALAR >::addImport(int              line,
                                                  std::string_view import,
                                                  std::string_view alias) {
        m_imports.push_back(new ImportCommand(line, import, alias));

        if (alias == "default") m_mainImport = m_imports.back();
      }

      template < GUM_Numeric GUM_SCALAR >
      void O3prmrContext< GUM_SCALAR >::addImport(int line, std::string_view import, bool ismain) {
        m_imports.push_back(new ImportCommand(line, import, import));

        if (ismain) m_mainImport = m_imports.back();
      }

      template < GUM_Numeric GUM_SCALAR >
      std::vector< O3prmrSession< GUM_SCALAR >* > O3prmrContext< GUM_SCALAR >::sessions() const {
        return m_sessions;
      }

      template < GUM_Numeric GUM_SCALAR >
      void O3prmrContext< GUM_SCALAR >::addSession(const O3prmrSession< GUM_SCALAR >& session) {
        m_sessions.push_back(new O3prmrSession< GUM_SCALAR >(session));
      }

      template < GUM_Numeric GUM_SCALAR >
      std::string O3prmrContext< GUM_SCALAR >::toString() const {
        std::string output;

        if (!m_package.empty()) {
          output += "package " + m_package + ";\n";
          output += "\n";
        }

        for (auto i = m_imports.begin(); i < m_imports.end(); i++)
          output += (*i)->toString() + "\n";

        output += "\n";

        for (auto i = m_sessions.begin(); i < m_sessions.end(); i++)
          output += (*i)->toString() + "\n";

        return output;
      }

      template < GUM_Numeric GUM_SCALAR >
      O3prmrContext< GUM_SCALAR >& O3prmrContext< GUM_SCALAR >::operator+=(const O3prmrContext& c) {
        const std::vector< ImportCommand* >& imports = c.imports();

        for (std::vector< ImportCommand* >::const_iterator i = imports.begin(); i != imports.end();
             i++)
          addImport(**i);

        const std::vector< O3prmrSession< GUM_SCALAR >* >& sessions = c.sessions();

        if (sessions.size() == 1 && sessions.back()->name() == "default") {
          *(this->m_sessions.back()) += *(sessions.back());
        } else
          for (auto i = sessions.begin(); i != sessions.end(); i++)
            addSession(**i);

        return *this;
      }

      /* ******************************************************************* */

      template < GUM_Numeric GUM_SCALAR >
      O3prmrSession< GUM_SCALAR >::O3prmrSession(std::string_view name) {
        m_name = name;
      }

      template < GUM_Numeric GUM_SCALAR >
      O3prmrSession< GUM_SCALAR >::O3prmrSession(const O3prmrSession< GUM_SCALAR >& s) {
        m_name = s.m_name;
        *this += s;
      }

      template < GUM_Numeric GUM_SCALAR >
      O3prmrSession< GUM_SCALAR >::~O3prmrSession() {
        for (Idx i = Size(m_commands.size()); i >= 1; i--)
          delete m_commands[i - 1];

        m_commands.clear();
      }

      template < GUM_Numeric GUM_SCALAR >
      std::string O3prmrSession< GUM_SCALAR >::name() const {
        return m_name;
      }

      template < GUM_Numeric GUM_SCALAR >
      std::vector< O3prmrCommand* > O3prmrSession< GUM_SCALAR >::commands() const {
        return m_commands;
      }

      template < GUM_Numeric GUM_SCALAR >
      void O3prmrSession< GUM_SCALAR >::addCommand(O3prmrCommand* command) {
        m_commands.push_back(command);
      }

      template < GUM_Numeric GUM_SCALAR >
      void O3prmrSession< GUM_SCALAR >::addObserve(int              line,
                                                   std::string_view leftValue,
                                                   std::string_view rightValue) {
        addCommand(new ObserveCommand< GUM_SCALAR >(line, leftValue, rightValue));
      }

      template < GUM_Numeric GUM_SCALAR >
      void O3prmrSession< GUM_SCALAR >::addUnobserve(int line, std::string_view value) {
        addCommand(new UnobserveCommand< GUM_SCALAR >(line, value));
      }

      template < GUM_Numeric GUM_SCALAR >
      void O3prmrSession< GUM_SCALAR >::addQuery(int line, std::string_view value) {
        addCommand(new QueryCommand< GUM_SCALAR >(line, value));
      }

      template < GUM_Numeric GUM_SCALAR >
      void O3prmrSession< GUM_SCALAR >::addSetEngine(int line, std::string_view value) {
        addCommand(new SetEngineCommand(line, value));
      }

      template < GUM_Numeric GUM_SCALAR >
      void O3prmrSession< GUM_SCALAR >::addSetGndEngine(int line, std::string_view value) {
        addCommand(new SetGndEngineCommand(line, value));
      }

      template < GUM_Numeric GUM_SCALAR >
      void O3prmrSession< GUM_SCALAR >::addCommand(const O3prmrCommand* command) {
        switch (command->type()) {
          case O3prmrCommand::RequestType::SetEngine :
            m_commands.push_back(new SetEngineCommand(*(SetEngineCommand*)command));
            break;

          case O3prmrCommand::RequestType::SetGndEngine :
            m_commands.push_back(new SetGndEngineCommand(*(SetGndEngineCommand*)command));
            break;

          case O3prmrCommand::RequestType::Observe :
            m_commands.push_back(
                new ObserveCommand< GUM_SCALAR >(*(ObserveCommand< GUM_SCALAR >*)command));
            break;

          case O3prmrCommand::RequestType::Unobserve :
            m_commands.push_back(
                new UnobserveCommand< GUM_SCALAR >(*(UnobserveCommand< GUM_SCALAR >*)command));
            break;

          case O3prmrCommand::RequestType::Query :
            m_commands.push_back(
                new QueryCommand< GUM_SCALAR >(*(QueryCommand< GUM_SCALAR >*)command));
            break;
        }
      }

      template < GUM_Numeric GUM_SCALAR >
      std::string O3prmrSession< GUM_SCALAR >::toString() const {
        std::string output;

        output += "request " + m_name + " {\n";

        for (std::vector< O3prmrCommand* >::const_iterator i = m_commands.begin();
             i < m_commands.end();
             i++) {
          output += "\t";
          output += (*i)->toString() + "\n";
        }

        output += "}\n";

        return output;
      }

      template < GUM_Numeric GUM_SCALAR >
      O3prmrSession< GUM_SCALAR >&
          O3prmrSession< GUM_SCALAR >::operator+=(const O3prmrSession< GUM_SCALAR >& c) {
        for (std::vector< O3prmrCommand* >::const_iterator i = c.m_commands.begin();
             i < c.m_commands.end();
             i++)
          addCommand(*i);

        return *this;
      }

      /* ******************************************************************* */

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