/*
 * 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_H_
#define SH4LT_FOLLOWER_H_

#include <atomic>
#include <filesystem>
#include <memory>
#include <mutex>
#include <string>

#include "./ipcs/reader.hpp"
#include "./logger/logger.hpp"
#include "./utils/threaded-wrapper.hpp"

namespace sh4lt {
namespace fs = std::filesystem;

class Follower {
 public:
  /**
   * Construct a Follower object that read a sh4lt, and handle connection/disconnection
   * of the writer. Information and data are provided asynchronously by the Follower.
   * though callbacks.
   *
   * \param   sockpath The socket file path of the Sh4lt to follow.
   * \param   cb   Callback to be triggered when a frame is published.
   * \param   osc  Callback to be triggered when the follower connects with the sh4lt writer.
   * \param   osd  Callback to be triggered when the follower disconnects from the sh4lt writer.
   * \param   log  Shared pointer to log object.
   *
   */
  Follower(const fs::path& sockpath,
           Reader::onData cb,
           Reader::onServerConnected osc,
           Reader::onServerDisconnected osd,
           logger::Logger::ptr log);

  /**
   * Destruct the follower and release resources acquired.
   */
  ~Follower();
  Follower() = delete;
  Follower(Follower&&) = delete;
  Follower(const Follower&) = delete;
  auto operator=(const Follower&) -> Follower& = delete;
  auto operator=(Follower&&) -> Follower& = delete;

  /**
   * Is connected.
   * \return true if the Follower is connected to a Writer, false otherwise.
   */
  [[nodiscard]] inline auto is_connected() const -> bool { return static_cast<bool>(reader_); };

  /**
   * Enable/disable monitor relaunch when writer has disconnected. When a follower is created,
   * monitor relaunch is enabled.
   *
   * \param monotor Set to true if monitor must be relaunched, false otherwise.
   */
  auto set_monitor_relaunch(bool monitor) -> void;

 private:
  std::mutex monitor_mutex_{};
  bool monitor_relaunch_{true};
  const unsigned int kMonitorTimeMs{5};
  std::mutex is_destructing_mutex_{};
  bool is_destructing_{false};
  logger::Logger::ptr log_;
  std::string path_;
  Reader::onData on_data_cb_;
  Reader::onServerConnected osc_;
  Reader::onServerDisconnected osd_;
  std::unique_ptr<ThreadedWrapper<>> monitor_{};
  std::atomic<bool> quit_{false};
  std::unique_ptr<Reader> reader_;
  void monitor();
  void on_server_disconnected();
};

}  // namespace sh4lt
#endif
