/*
* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of The Linux Foundation nor the names of its
*       contributors may be used to endorse or promote products derived
*       from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <utils/fence.h>
#include <debug_handler.h>
#include <assert.h>
#include <string>
#include <vector>
#include <algorithm>

#define __CLASS__ "Fence"

namespace sdm {

#define ASSERT_IF_NO_BUFFER_SYNC(x) if (!x) { assert(false); }

BufferSyncHandler* Fence::g_buffer_sync_handler_ = nullptr;
std::vector<std::weak_ptr<Fence>> Fence::wps_;

Fence::Fence(int fd, const string &name) : fd_(fd), name_(name) {
}

Fence::~Fence() {
  close(fd_);

  // erase all expired weak references.
  /*
  wps_.erase(std::remove_if(wps_.begin(), wps_.end(), [](const std::weak_ptr<Fence> &wp) {
    return wp.expired();
  }), wps_.end());
  */
}

void Fence::Set(BufferSyncHandler *buffer_sync_handler) {
  g_buffer_sync_handler_ = buffer_sync_handler;
}

shared_ptr<Fence> Fence::Create(int fd, const string &name) {
  // Do not create Fence object for invalid fd, so that nullptr can be used for invalid fences.
  if (fd < 0) {
    return nullptr;
  }

  shared_ptr<Fence> fence(new Fence(fd, name));
  if (!fence) {
    close(fd);
  }

  // wps_.push_back(fence);

  return fence;
}

int Fence::Dup(const shared_ptr<Fence> &fence) {
  return (fence ? dup(fence->fd_) : -1);
}

int Fence::Get(const shared_ptr<Fence> &fence) {
  return (fence ? fence->fd_ : -1);
}

shared_ptr<Fence> Fence::Merge(const shared_ptr<Fence> &fence1, const shared_ptr<Fence> &fence2) {
  ASSERT_IF_NO_BUFFER_SYNC(g_buffer_sync_handler_);

  // Sync merge will return a new unique fd if source fds are same.
  int fd1 = fence1 ? fence1->fd_ : -1;
  int fd2 = fence2 ? fence2->fd_ : -1;
  int merged = -1;
  std::string name = "merged[" + to_string(fd1) + ", " + to_string(fd2) + "]";

  g_buffer_sync_handler_->SyncMerge(fd1, fd2, &merged);

  return Create(merged, name);
}

shared_ptr<Fence> Fence::Merge(const std::vector<shared_ptr<Fence>> &fences, bool ignore_signaled) {
  ASSERT_IF_NO_BUFFER_SYNC(g_buffer_sync_handler_);

  shared_ptr<Fence> merged_fence = nullptr;
  for (auto &fence : fences) {
    if (ignore_signaled && (Fence::Wait(fence, 0) == kErrorNone)) {
      continue;
    }

    merged_fence = Fence::Merge(fence, merged_fence);
  }

  return merged_fence;
}

DisplayError Fence::Wait(const shared_ptr<Fence> &fence) {
  ASSERT_IF_NO_BUFFER_SYNC(g_buffer_sync_handler_);

  return g_buffer_sync_handler_->SyncWait(Fence::Get(fence), 1000);
}

DisplayError Fence::Wait(const shared_ptr<Fence> &fence, int timeout) {
  ASSERT_IF_NO_BUFFER_SYNC(g_buffer_sync_handler_);

  return g_buffer_sync_handler_->SyncWait(Fence::Get(fence), timeout);
}

Fence::Status Fence::GetStatus(const shared_ptr<Fence> &fence) {
  ASSERT_IF_NO_BUFFER_SYNC(g_buffer_sync_handler_);

  if (!fence) {
    return Fence::Status::kSignaled;
  }

  // Treat only timeout error as pending, assume other errors as signaled.
  return (g_buffer_sync_handler_->SyncWait(Fence::Get(fence), 0) == kErrorTimeOut ?
                                    Fence::Status::kPending : Fence::Status::kSignaled);
}

string Fence::GetStr(const shared_ptr<Fence> &fence) {
  return std::to_string(Fence::Get(fence));
}

void Fence::Dump(std::ostringstream *os) {
  ASSERT_IF_NO_BUFFER_SYNC(g_buffer_sync_handler_);

  *os << "\n------------Active Fences Info---------";
  /*
  for (auto &wp : wps_) {
    *os << "\n";
    shared_ptr<Fence> fence = wp.lock();
    if (!fence) {
      continue;
    }
    *os << "FD: " << fence->fd_;
    *os << ", name: " << fence->name_;
    *os << ", use_count: " << fence.use_count() - 1;   // Do not count wp lock reference
    *os << ", ";
    g_buffer_sync_handler_->GetSyncInfo(fence->fd_, os);
  }
  */
  *os << "\n---------------------------------------\n";
}

Fence::ScopedRef::~ScopedRef() {
  for (int dup_fd : dup_fds_) {
    close(dup_fd);
  }
}

int Fence::ScopedRef::Get(const shared_ptr<Fence> &fence) {
  int dup_fd = Fence::Dup(fence);
  if (dup_fd >= 0) {
    dup_fds_.push_back(dup_fd);
  }

  return dup_fd;
}

}  // namespace sdm
