/*
 * 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_SYSV_SEM_H_
#define SH4LT_SYSV_SEM_H_

#include <sys/ipc.h>
#include <sys/sem.h>
#include <time.h>

#include <functional>

#include "../logger/logger.hpp"
#include "../utils/safe-bool-idiom.hpp"
#include "./unix-socket-server.hpp"

namespace sh4lt {

class ReadLock;
class TimedWriteLock;
class WriteLock;

auto force_semaphore_cleaning(key_t key, logger::Logger* log) -> bool;

class sysVSem : public SafeBoolIdiom {
  friend ReadLock;
  friend TimedWriteLock;
  friend WriteLock;

 public:
  sysVSem(key_t key,
          logger::Logger* log,
          bool owner = false,
          mode_t unix_permission = UnixSocketServer::kDefaultPermission);
  ~sysVSem() override;
  sysVSem() = delete;
  sysVSem(sysVSem&&) = delete;
  sysVSem(const sysVSem&) = delete;
  auto operator=(const sysVSem&) -> sysVSem& = delete;
  auto operator=(sysVSem&&) -> sysVSem& = default;

  void cancel_commited_reader();

 private:
  key_t key_;
  bool owner_;
  int semid_;
  logger::Logger* log_;
  [[nodiscard]] auto is_valid() const -> bool final;
};

class ReadLock : public SafeBoolIdiom {
 public:
  ReadLock(sysVSem* sem);
  ~ReadLock() override;
  ReadLock() = delete;
  ReadLock(ReadLock&&) = delete;
  ReadLock(const ReadLock&) = delete;
  auto operator=(const ReadLock&) -> ReadLock& = delete;
  auto operator=(ReadLock&&) -> ReadLock& = delete;

 private:
  sysVSem* sem_;
  bool valid_{true};
  [[nodiscard]] auto is_valid() const -> bool final { return valid_; };
};

class TimedWriteLock : public SafeBoolIdiom {
 public:
  TimedWriteLock(sysVSem* sem, const struct timespec* timeout);
  ~TimedWriteLock() override;
  TimedWriteLock() = delete;
  TimedWriteLock(TimedWriteLock&&) = delete;
  TimedWriteLock(const TimedWriteLock&) = delete;
  auto operator=(const TimedWriteLock&) -> TimedWriteLock& = delete;
  auto operator=(TimedWriteLock&&) -> TimedWriteLock& = default;

  auto commit_readers(short num_readers) -> bool;

 private:
  sysVSem* sem_;
  bool valid_{true};
  [[nodiscard]] auto is_valid() const -> bool final { return valid_; };
};

class WriteLock : public SafeBoolIdiom {
 public:
  WriteLock(sysVSem* sem);
  ~WriteLock() override;
  WriteLock() = delete;
  WriteLock(WriteLock&&) = delete;
  WriteLock(const WriteLock&) = delete;
  auto operator=(const WriteLock&) -> WriteLock& = delete;
  auto operator=(WriteLock&&) -> WriteLock& = default;

  auto commit_readers(short num_readers) -> bool;

 private:
  sysVSem* sem_;
  bool valid_{true};
  [[nodiscard]] auto is_valid() const -> bool final { return valid_; };
};

}  // namespace sh4lt
#endif
