/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 *
 * This program 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 Lesser General Public License for more details.
 */

#ifndef SH4LT_TYPE_H_
#define SH4LT_TYPE_H_

#include <filesystem>
#include <map>
#include <string>

#include "../infotree/information-tree.hpp"
#include "../utils/any.hpp"
#include "../utils/safe-bool-log.hpp"

namespace sh4lt {
namespace fs = std::filesystem;

/*! ShType class provides a description of data shared with Sh4lt. It has a
media specification (a string) and a list composed of key/value elements.

For media specification, mime types compatible with GStreamer are
highly suggested. Custom data type can use a specification as the following
"application/x-myformat".

It provides (de)serialization with JSON.
Example of use can be found in \example check-shtype.cpp
*/
class ShType : public SafeBoolLog {
 public:
  /**
   * Default constructor. It produces an invalid object since media description
   * is mandatory.
   */
  ShType() = default;

  /**
   * Construct a ShType in the default group without description.
   *
   * \param media The media type.
   * \param label The media label.
   **/
  ShType(std::string media, std::string label);

  /**
   * Construct a ShType without description.
   *
   * \param media The media type.
   * \param label The media label.
   * \param group_labeg The group label.
   **/
  ShType(std::string media, std::string label, std::string group_label);

  /**
   * Construct a ShType.
   *
   * \param media The media type.
   * \param label The media label.
   * \param group_labeg The group label.
   * \param description The Sh4lt description.
   **/
  ShType(std::string media, std::string label, std::string group_label, std::string description);

  /**
   * Get the ShType described in an InfoTree.
   *
   * \return The information tree object.
   **/
  auto as_info_tree() const -> InfoTree::ptr;

  /**
   * Obtain a ShType from serialization.
   *
   * \param serialized A string specification (JSON) of a Shtype.
   *
   * \return The corresponding Shtype.
   **/
  static auto deserialize(const std::string& serialized) -> ShType;

  /**
   * Obtain a string representation of a ShType.
   *
   * \param shtype The ShType to serialize.
   *
   * \return The resulting serialization.
   **/
  static auto serialize(const ShType& shtype) -> std::string;

  /**
   * Get the Sh4lt media specification.
   *
   * \return The media specification.
   **/
  [[nodiscard]] auto media() const -> std::string;

  /**
   * Get the Sh4lt label.
   *
   * \return The media label.
   **/
  [[nodiscard]] auto label() const -> std::string;

  /**
   * Get the Sh4lt group label.
   *
   * \return The media group label.
   **/
  [[nodiscard]] auto group() const -> std::string;

  /**
   * Get the Sh4lt description.
   *
   * \return The media group label.
   **/
  [[nodiscard]] auto description() const -> std::string;

  /**
   * Get Sh4lt id.
   *
   * \return The id.
   **/
  [[nodiscard]] auto id() const -> std::size_t;

  /**
   * Provide the absolute file path for the Sh4lt Unix socket, using internal labels.
   *
   * \return The absolute file path, or empty in case of issue with the socket path directory.
   *         Check get_log_for_path_issue for message.
   **/
  [[nodiscard]] auto path() const -> fs::path;

  /**
   * Get the value for a given key.
   *
   * \param key The key.
   *
   * \return The value, or empty Any.
   **/
  [[nodiscard]] auto get_prop(const std::string& key) const -> Any;

  /**
   * Get the custom type for a given key.
   *
   * \param key The key.
   *
   * \return The value, or empty if no custom type is specified for this key.
   **/
  [[nodiscard]] auto get_custom_type(const std::string& key) const -> std::string;

  /**
   * Get the list of keys for the properties.
   *
   * \return the list of keys.
   **/
  [[nodiscard]] auto get_property_keys() const -> std::vector<std::string>;

  /**
   * Get the properties.
   *
   * \return the properties in a map.
   **/
  [[nodiscard]] auto get_properties() const -> std::map<std::string, Any>;

  /**
   * Get the custom property types.
   *
   * \return the custom property types.
   **/
  [[nodiscard]] auto get_custom_types() const -> std::map<std::string, std::string>;

  /**
   * Set the media mime type.
   *
   * \param media The media mime type.
   **/
  void set_media(const std::string& media);

  /**
   * Set the media label.
   *
   * \param media The media label.
   **/
  void set_label(const std::string& label);

  /**
   * Set the media group label.
   *
   * \param media The media group label.
   **/
  void set_group(const std::string& label);

  /**
   * Set the Sh4lt description.
   *
   * \param media The Sh4lt description.
   **/
  void set_description(const std::string& description);

  /**
   * Set a property.
   *
   * \param key The property key.
   * \param value The property value.
   **/
  template <typename T>
  void set_prop(const std::string& key, T&& value) {
    properties_[key] = std::forward<T>(value);
  }
  void set_prop(const std::string& key, const std::string& value);
  void set_prop(const std::string& key, const char* value);

  /**
   * Set a property with a custom type.
   *
   * \param key The property key.
   * \param custom_type The property custom type name.
   * \param value The property value.
   **/
  void set_custom_prop(const std::string& key,
                       const std::string& custom_type,
                       const std::string& value);

  /**
   * Set the custom type of a property.
   *
   * \param key The property key.
   * \param custom_type The property custom type.
   **/
  void set_custom_type(const std::string& key, const std::string& custom_type);

  /**
   * Get default group label
   *
   * \return The default group label
   **/
  static auto default_group() -> std::string;

  /**
   * Provide the absolute file path for the Sh4lt Unix socket.
   *
   * \param label The Sh4lt label.
   * \param group_label The Sh4lt group label.
   *
   * \return The absolute file path.
   **/
  static auto get_path(const std::string& label, const std::string& group_label) -> fs::path;

  /**
   * Provide the absolute path for the Sh4lt Unix socket.
   *
   * \return The sh4lt directory path.
   **/
  static auto get_socket_dir() -> fs::path;

  /**
   * Provide the log for issue with the socket path directory.
   *
   * \return The log message (empty if no issue found).
   **/
  static auto get_log_for_path_issue() -> std::string;

 private:
  std::string media_{};
  std::string label_{};
  std::string group_{};
  std::string description_{};
  std::map<std::string, Any> properties_{};
  std::map<std::string, std::string> custom_types_{};
  [[nodiscard]] auto is_valid() const -> bool final;
};

}  // namespace sh4lt
#endif
