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

#include "./follower.hpp"

#include <thread>
#include <utility>

#include "./ipcs/file-monitor.hpp"
#include "./ipcs/unix-socket-server.hpp"
#include "./writer.hpp"

namespace sh4lt {

Follower::Follower(const fs::path& sockpath,
                   Reader::onData cb,
                   Reader::onServerConnected osc,
                   Reader::onServerDisconnected osd,
                   logger::Logger::ptr log)
    : log_(log),
      path_(sockpath.string()),
      on_data_cb_(cb),
      osc_(osc),
      osd_(osd),
      reader_(fileMonitor::is_unix_socket(path_, log_.get())
                  ? std::make_unique<Reader>(
                        path_, on_data_cb_, osc_, [&]() { on_server_disconnected(); }, log_.get())
                  : nullptr) {
  if (!reader_ || !(*reader_.get())) {
    monitor_ = std::make_unique<ThreadedWrapper<>>();
    monitor_->run_async([&]() { monitor(); });
  }
}

Follower::~Follower() {
  std::lock_guard<std::mutex> lock(is_destructing_mutex_);
  is_destructing_ = true;
  quit_.store(true);
}

void Follower::set_monitor_relaunch(bool do_monitor) {
  std::unique_lock<std::mutex> lock(monitor_mutex_);
  monitor_relaunch_ = do_monitor;
  if (monitor_relaunch_ && (!reader_ || !(*reader_.get()))) {
    quit_.store(true);
    monitor_.reset(nullptr);
    quit_.store(false);
    monitor_ = std::make_unique<ThreadedWrapper<>>();
    monitor_->run_async([&]() { monitor(); });
  }
}

void Follower::monitor() {
  while (!quit_.load()) {
    std::unique_lock<std::mutex> lock(monitor_mutex_);
    if (fileMonitor::is_unix_socket(path_, log_.get())) {
      // log_->debug("file detected, creating reader");
      reader_.reset(nullptr);
      reader_ = std::make_unique<Reader>(
          path_, on_data_cb_, osc_, [&]() { on_server_disconnected(); }, log_.get());
      if (*reader_.get()) {
        quit_.store(true);
      } else {
        reader_.reset(nullptr);
      }
    }
    if (!quit_.load()) std::this_thread::sleep_for(std::chrono::milliseconds(kMonitorTimeMs));
  }  // end while
}

void Follower::on_server_disconnected() {
  log_->debug("follower on_server_disconnected");
  // calling user callback
  if (osd_) osd_();
  // starting monitor
  std::lock_guard<std::mutex> lock(is_destructing_mutex_);
  if (!is_destructing_ && monitor_relaunch_) {
    std::unique_lock<std::mutex> lock(monitor_mutex_);
    quit_.store(true);
    monitor_.reset(nullptr);
    quit_.store(false);
    monitor_ = std::make_unique<ThreadedWrapper<>>();
    monitor_->run_async([&]() { monitor(); });
    quit_.store(false);
  }
}

}  // namespace sh4lt
