/*
 * 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_UNIX_SOCKET_SERVER_H_
#define SH4LT_UNIX_SOCKET_SERVER_H_

#include <atomic>
#include <functional>
#include <mutex>
#include <set>
#include <string>
#include <vector>

#include "../logger/logger.hpp"
#include "../time.hpp"
#include "../utils/safe-bool-idiom.hpp"
#include "../utils/threaded-wrapper.hpp"
#include "./unix-socket-protocol.hpp"
#include "./unix-socket.hpp"

namespace sh4lt {

auto force_sockserv_cleaning(const std::string& path, logger::Logger* log) -> bool;

class UnixSocketServer : public SafeBoolIdiom {
 public:
  UnixSocketServer(std::string path,
                   UnixSocketProtocol::ServerSide* proto,
                   logger::Logger* log,
                   std::function<void(int)> on_client_error = nullptr,
                   mode_t unix_permissions = kDefaultPermission,
                   int max_pending_cnx = kMaxPendingConnections);
  ~UnixSocketServer() override;
  UnixSocketServer() = delete;
  UnixSocketServer(UnixSocketServer&&) = delete;
  UnixSocketServer(const UnixSocketServer&) = delete;
  auto operator=(const UnixSocketServer&) -> UnixSocketServer& = delete;
  auto operator=(UnixSocketServer&&) -> UnixSocketServer& = delete;

  [[nodiscard]] auto num_of_clients() const -> size_t;
  void start_serving();
  // return true if at least one notification has been sent
  auto notify_update(size_t size, Time::info_t* time_info, bool force_update) -> short;

  static const mode_t kDefaultPermission = 0600;

 private:
  static const int kMaxPendingConnections{10};
  static const int kClientTimeoutMicrosec{10000};
  logger::Logger* log_;
  std::string path_;
  UnixSocket socket_;
  int max_pending_cnx_;
  bool is_binded_{false};
  bool is_listening_{false};
  std::atomic_short quit_{0};
  std::vector<int> clients_{};
  mutable std::mutex clients_mutex_{};
  std::set<int> clients_notified_{};
  std::set<int> pending_clients_{};
  UnixSocketProtocol::ServerSide* proto_;
  std::function<void(int)> on_client_error_;
  std::unique_ptr<ThreadedWrapper<>> done_{};
  [[nodiscard]] auto is_valid() const -> bool final;
  void client_interaction();
};

}  // namespace sh4lt
#endif
