/*
 * 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_FOLLOWER_STAT_H_
#define SH4LT_FOLLOWER_STAT_H_

#include <chrono>
#include <mutex>

#include "../follower.hpp"
#include "../infotree/information-tree.hpp"
#include "../infotree/json-serializer.hpp"
#include "../utils/data-printer.hpp"

namespace sh4lt::monitor {

using namespace std::chrono;
class FollowerStat {
 public:
  /**
   * Construct the FollowerStat object.
   *
   * \param path Directory path to watch
   **/
  FollowerStat(logger::Logger::ptr log, fs::path path);

  FollowerStat() = delete;

  /**
   * Get the current state of the statistics.
   * Note that bytes-per-seconds and frame-per-seconds are updated only when the
   * kMinUpdateTimeInMSec duration has passed since last update.
   *
   * Here follows, as an exemple, a returned information tree, in the JSON format:
   * {
   *    'connected': True,
   *    'shtype': {
   *         'group': 'Default',
   *         'label': 'video',
   *         'media': 'video/x-raw',
   *         'property': {
   *               'format': 'ABGR',
   *               'framerate': '30/1',
   *               'height': 1080,
   *               'width': 1920
   *          }
   *     },
   *     'stats': {
   *          'Mbps': 1987.07275390625,
   *          'buffer_date': 24981933333333,
   *          'buffer_number': 749458,
   *          'speed': 1.0,
   *          'data': '29 2D 31 35 3A 3F 44 49 4F 55 ...',
   *          'dur_ms': 33.33333206176758,
   *          'fps': 29.945999145507812,
   *          'reup': 0,
   *          'size_kB': 8294.400390625,
   *          'system_clock_date': 1722391410082696450,
   *          'timecode': '06:56:21.933',
   *          'total_size': 3035750400
   *      }
   *   }
   *
   * where :
   *    - connected is true if the writer is connected
   *    - shtype is the writer shtype as provided by the ShType::as_info_tree() method
   *    - buffer_date, buffer_number, dur_ms, size_KB, system_clock_date, speed and timecode are
   *      information related to the last received buffer before the call to stats
   *    - data is the first value of the first buffer received after the last call to stats
   *    - Mbps (megabit-per-second) and fps (frame-per-second) profide throughput in bits and frame
   *    - total_size is the total size in bytes received since the creation of the follower stat
   *    - reup is the number of times the writer did re-appear
   *
   * \return The information tree with the statistics
   **/
  auto stats() -> InfoTree::ptr;

  /**
   * Get configuration for value printing.
   *
   * Here is an example configuration:
   * ```
   * {
   *    'format' = 'x',
   *    'separator' = ' ',
   *    'precision' = 3,
   *    'min_width' = 0,
   *    'num_vals' = 10
   * }
   **/
  auto data_printer_configuration_ptr() const -> const InfoTree*;

  /**
   * Set formating options for the values of the data printer.
   *
   * See `::Sh4lt::utils::make_data_printer_fun` for documentation.
   *
   */
  bool set_data_printer_configuration(std::string value_format,
                                      std::string separator,
                                      size_t precision,
                                      size_t min_width);
  /**
   * Set number of value to be printed.
   *
   *\param num_val The number of value to be printed. 0 for all values.
   **/
  void set_num_value_to_print(size_t num_val);

  /**
   * Provide the ShType of the followed writer. Can be incomplete if the ShType has not been
   * received.
   *
   * \return The ShType
   **/
  auto shtype() const -> ShType;

  /**
   * ShType has been received from the Sh4lt writer.
   *
   * \return True if ShType has been reveived, false otherwise.
   **/
  auto has_shtype() const -> bool;

  /**
   * Provide the group of the followed writer.
   *
   * \return The group or empty string if the ShType has not been received.
   **/
  auto group() const -> std::string;

  /**
   * Provide the label of the followed writer.
   *
   * \return The label or empty string if the ShType has not been received.
   **/
  auto label() const -> std::string;

  /**
   * Provide the path of the followed writer
   *
   * \return The path
   **/
  auto path() const -> fs::path;

  /**
   * Provide the id of the followed writer
   *
   * \return The id, or 0 if the shtype has not been set.
   **/
  auto id() const -> std::size_t;

 private:
  static const size_t kMinUpdateTimeInMSec{2000};
  logger::Logger::ptr log_;
  mutable std::mutex mutex_{};
  fs::path path_;
  bool is_connected_{false};
  bool has_shtype_{false};
  size_t total_size_{0};
  size_t size_{0};
  size_t size_since_last_update_{0};
  size_t frame_count_since_last_update_{0};
  size_t re_up_{0};
  float fps_{-1};
  float bps_{-1};
  time_point<system_clock> bps_fps_update_;
  int64_t buffer_number_{-1};
  int64_t buffer_date_{-1};
  int64_t buffer_duration_{-1};
  float speed_{1.0};
  int64_t system_clock_date_{-1};
  std::string data_print_{};
  ShType shtype_{};
  // data printer values
  InfoTree::ptr data_printer_conf_{::sh4lt::infotree::json::deserialize(
      R"({ 
           "format" : "x", 
           "separator" : " ", 
           "precision" : 3, 
           "min_width" : 0, 
           "num_vals" : 10 
         })")};
  size_t dp_num_vals_{data_printer_conf_->branch_read_data<size_t>("num_vals")};
  ::sh4lt::utils::data_printer_t data_printer_{*::sh4lt::utils::make_data_printer_fun(
      data_printer_conf_->branch_read_data<std::string>("format"),
      data_printer_conf_->branch_read_data<std::string>("separator"),
      data_printer_conf_->branch_read_data<size_t>("precision"),
      data_printer_conf_->branch_read_data<size_t>("min_width"))};
  Follower follower_;

  void on_data(void* data, size_t size, const sh4lt_time_info_t* info);
  void on_shtype(const ShType& shtype);
  void on_disconnected();
  static auto format_date(int64_t date) -> std::string;
  void update_fps_bps();
};

}  // namespace sh4lt::monitor
#endif
