/*
 * 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 "./reader.hpp"

#include "../shtype/shtype-from-gst-caps.hpp"

namespace sh4lt {

Reader::Reader(const std::string& path,
               onData cb,
               onServerConnected osc,
               onServerDisconnected osd,
               logger::Logger* log)
    : log_(log),
      path_(path),
      on_data_cb_(std::move(cb)),
      on_server_connected_cb_(std::move(osc)),
      on_server_disconnected_cb_(std::move(osd)),
      proto_([this]() { on_server_connected(); },
             [this]() { on_server_disconnected(); },
             [this](size_t size, const Time::info_t* time_info, bool force_update) {
               if (size != cur_size_ || force_update)  // a resize has been done
                 shm_ = std::make_unique<sysVShm>(ftok(path_.c_str(), 'n'),
                                                  0,
                                                  log_,
                                                  /* owner = */ false);
               cur_size_ = size;
               on_buffer(this->sem_.get(), size, time_info);
             }),  // read when update is received
      cli_(std::make_unique<UnixSocketClient>(path, log_)) {
  if (!cli_ || !(*cli_.get())) {
    // log_->debug("reader initialization failed (initializing socket client)");
    cli_.reset();
    return;
  }
  shm_ = std::make_unique<sysVShm>(ftok(path.c_str(), 'n'), 0, log_, /* owner = */ false);
  sem_ = std::make_unique<sysVSem>(ftok(path.c_str(), 'm'), log_, /* owner = */ false);
  if (!*shm_.get() || !*sem_.get() || !cli_->start(&proto_)) {
    // log_->debug("reader initialization failed");
    cli_.reset();
    shm_.reset();
    sem_.reset();
    return;
  }
  is_valid_ = true;
  log_->debug("reader initialization done");
}

void Reader::on_server_connected() {
  const auto& data = proto_.data_.user_data_.data();
  log_->debug(
      "received server info, shm_size %, type %", std::to_string(proto_.data_.shm_size_), data);
  auto shtype = ShType::deserialize(data);
  if (!shtype.msg().empty()) {
    log_->warning("issue with the writer sh4lt type definition %:", shtype.msg());
    log_->debug("trying to interpret type as a serialized GStreamer caps ()");
    // using the path as label
    shtype = shtype::shtype_from_gst_caps(data, path_);
    if (!shtype.msg().empty()) {
      log_->error("writer % is not compatible with Sh4lt: %", path_, shtype.msg());
    }
  }
  if (on_server_connected_cb_) on_server_connected_cb_(shtype);
}

void Reader::on_server_disconnected() {
  log_->debug("disconnected from server");
  if (on_server_disconnected_cb_) on_server_disconnected_cb_();
}

auto Reader::on_buffer(sysVSem* sem, size_t size, const Time::info_t* time_info) -> bool {
  ReadLock lock(sem);
  if (!lock) return false;
  if (on_data_cb_) on_data_cb_(shm_->get_mem(), size, time_info);
  return true;
}

}  // namespace sh4lt
