/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "drmhwc"

#include "DrmHwcTwo.h"

#include <cinttypes>

#include "backend/Backend.h"
#include "utils/log.h"

namespace android {

HWC2::Error DrmHwcTwo::RegisterCallback(int32_t descriptor,
                                        hwc2_callback_data_t data,
                                        hwc2_function_pointer_t function) {
  switch (static_cast<HWC2::Callback>(descriptor)) {
    case HWC2::Callback::Hotplug: {
      hotplug_callback_ = std::make_pair(HWC2_PFN_HOTPLUG(function), data);
      if (function != nullptr) {
        GetResMan().Init();
      } else {
        GetResMan().DeInit();
        /* Headless display may still be here. Remove it! */
        if (Displays().count(kPrimaryDisplay) != 0) {
          Displays()[kPrimaryDisplay]->Deinit();
          Displays().erase(kPrimaryDisplay);
        }
      }
      break;
    }
    case HWC2::Callback::Refresh: {
      refresh_callback_ = std::make_pair(HWC2_PFN_REFRESH(function), data);
      break;
    }
    case HWC2::Callback::Vsync: {
      vsync_callback_ = std::make_pair(HWC2_PFN_VSYNC(function), data);
      break;
    }
#if __ANDROID_API__ > 29
    case HWC2::Callback::Vsync_2_4: {
      vsync_2_4_callback_ = std::make_pair(HWC2_PFN_VSYNC_2_4(function), data);
      break;
    }
    case HWC2::Callback::VsyncPeriodTimingChanged: {
      period_timing_changed_callback_ = std::
          make_pair(HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED(function), data);
      break;
    }
#endif
    default:
      break;
  }
  return HWC2::Error::None;
}

void DrmHwcTwo::SendHotplugEventToClient(hwc2_display_t displayid,
                                         DisplayStatus display_status) {
  auto hc = hotplug_callback_;

  if (hc.first != nullptr && hc.second != nullptr) {
    /* For some reason HWC Service will call HWC2 API in hotplug callback
     * handler. This is the reason we're using recursive mutex.
     */
    hc.first(hc.second, displayid,
             display_status ? HWC2_CONNECTION_CONNECTED
                            : HWC2_CONNECTION_DISCONNECTED);
  }
}

void DrmHwcTwo::SendVsyncEventToClient(
    hwc2_display_t displayid, int64_t timestamp,
    [[maybe_unused]] uint32_t vsync_period) const {
  /* vsync callback */
#if __ANDROID_API__ > 29
  if (vsync_2_4_callback_.first != nullptr &&
      vsync_2_4_callback_.second != nullptr) {
    vsync_2_4_callback_.first(vsync_2_4_callback_.second, displayid, timestamp,
                              vsync_period);
  } else
#endif
      if (vsync_callback_.first != nullptr &&
          vsync_callback_.second != nullptr) {
    vsync_callback_.first(vsync_callback_.second, displayid, timestamp);
  }
}

void DrmHwcTwo::SendVsyncPeriodTimingChangedEventToClient(
    [[maybe_unused]] hwc2_display_t displayid,
    [[maybe_unused]] int64_t timestamp) const {
#if __ANDROID_API__ > 29
  hwc_vsync_period_change_timeline_t timeline = {
      .newVsyncAppliedTimeNanos = timestamp,
      .refreshRequired = false,
      .refreshTimeNanos = 0,
  };
  if (period_timing_changed_callback_.first != nullptr &&
      period_timing_changed_callback_.second != nullptr) {
    period_timing_changed_callback_
        .first(period_timing_changed_callback_.second, displayid, &timeline);
  }
#endif
}

void DrmHwcTwo::SendRefreshEventToClient(hwc2_display_t displayid) {
  if (refresh_callback_.first != nullptr &&
      refresh_callback_.second != nullptr) {
    refresh_callback_.first(refresh_callback_.second, displayid);
  }
}

}  // namespace android
