/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2024, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

#pragma once

#include <mrpt/gui/CDisplayWindow3D.h>

#include <map>
#include <string>

#include "CWindowManager.h"

namespace mrpt::graphslam::detail
{
/**\brief Generic class for tracking the total number of edges for different
 * tpes of edges and for storing visualization-related information for each
 * type
 *
 * \ingroup mrpt_graphslam_grp
 */
class CEdgeCounter
{
 public:
  using iterator = std::map<std::string, int>::iterator;
  using const_iterator = std::map<std::string, int>::const_iterator;

  CEdgeCounter();
  ~CEdgeCounter() = default;
  /**\brief Provide the instance with a CWindowManager.
   */
  void setWindowManagerPtr(mrpt::graphslam::CWindowManager* win_manager);
  /**\brief State how many of the existing edges have been removed.
   *
   * Method is to be called after CNetworkOfPoses::collapseDuplicatedEdges
   * method has been executed.
   */
  void setRemovedEdges(int removed_edges);
  /**\brief Method for manually setting the number of loop closures
   * registered so far.
   */
  void setLoopClosureEdgesManually(int num_loop_closures);
  /**\brief Returns the edges that form loop closures in the current graph.
   */
  int getLoopClosureEdges() const;
  /**\brief Return the total amount of registered edges.
   * \sa getNumForEdgeType, getLoopClosureEdges
   */
  int getTotalNumOfEdges() const;
  /**\brief Return the total amount of registered edges.
   *
   * \sa getNumForEdgeType, getLoopClosureEdges
   */
  void getTotalNumOfEdges(int* total_num_edges) const;
  /**\brief Return the number of edges for the specified type.
   *
   * \exception std::exception If edge is not found
   * \sa getTotalNumOfEdges
   */
  int getNumForEdgeType(const std::string& name) const;
  /** Return the number of edges for the specified type.
   *
   * \exception std::exception If edge is not found
   * \sa getTotalNumOfEdges
   */
  void getNumForEdgeType(const std::string& name, int* total_num);
  /**\brief Set number of a specific edge type manually.
   *
   * Handy for not having to call addEdge multiple times in a row.
   *
   * \sa addEdge
   */
  void setEdgesManually(const std::string& name, int num_of_edges);
  /**\brief Increment the number of edges for the specified type.
   *
   * \exception std::exception If edge exists and \b is_new is True
   *
   * \sa setEdgesManually
   */
  void addEdge(const std::string& name, bool is_loop_closure = false, bool is_new = false);
  /**\brief Explicitly register a new edge type.
   */
  void addEdgeType(const std::string& name);
  /**\brief Reset the state of the CEdgeCounter instance.
   */
  void clearAllEdges();
  /**\brief Dump a report of the registered, so far, edges to the console.
   *
   * \sa getAsString
   */
  void dumpToConsole() const;
  /**\brief Fill the provided string with a detailed report of the
   * registered, so far, edges.
   */
  void getAsString(std::string* str_out) const;
  /**\brief Return a detailed report of the registered, so far, edges in a
   * string representation.
   */
  std::string getAsString() const;

  // VISUALIZATION RELATED METHODS
  // ////////////////////////////

  /**\brief Add the textMessage parameters to the object
   * All the names in the given std::maps have to be already
   * specified and added in the object via addEdge with is_new=true or
   * addEdgeType
   *
   * \exception std::exception If a name in the provided std::map doesn't
   * already exist
   */
  void setTextMessageParams(
      const std::map<std::string, double>& name_to_offset_y,
      const std::map<std::string, int>& name_to_text_index);

  /**\brief Handle the extra visualization parameters for the total number of
   * edges and for loop closures and then passes execution to the other
   * setTextMessageParams function.
   */
  void setTextMessageParams(
      const std::map<std::string, double>& name_to_offset_y,
      const std::map<std::string, int>& name_to_text_index,
      double offset_y_total_edges,
      int text_index_total_edges,
      double offset_y_loop_closures,
      int text_index_loop_closures);

  /**\brief Instance Iterators */
  inline iterator begin() { return m_name_to_edges_num.begin(); }
  inline const_iterator cbegin() const { return m_name_to_edges_num.cbegin(); }
  inline iterator end() { return m_name_to_edges_num.end(); }
  inline const_iterator cend() const { return m_name_to_edges_num.cend(); }

 private:
  /**\brief Update the given CDisplayWindow3D with the edges registered so
   * far.
   */
  void updateTextMessages() const;

  mrpt::gui::CDisplayWindow3D* m_win = nullptr;
  mrpt::graphslam::CWindowManager* m_win_manager = nullptr;

  /**\brief Map edge name <=> num of edges
   *
   * Tracking number of edges
   */
  std::map<std::string, int> m_name_to_edges_num;
  int m_num_loop_closures = 0;
  int m_unique_edges = 0;

  // visualization std::maps
  std::map<std::string, double> m_name_to_offset_y;
  std::map<std::string, int> m_name_to_text_index;

  bool m_has_read_textmessage_params;

  // specifics to loop closures, total edges
  bool m_display_total_edges = false;
  bool m_display_loop_closures = false;
  int m_offset_y_total_edges = 0;
  int m_offset_y_loop_closures = 0;
  int m_text_index_total_edges = 0;
  int m_text_index_loop_closures = 0;
};
}  // namespace mrpt::graphslam::detail
