/*
 * 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.
 */

#include "base_composer.h"

#include <string.h>

#include <cutils/log.h>
#include <hardware/gralloc.h>

#include "common/vsoc/lib/screen_region_view.h"
#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"

using vsoc::screen::ScreenRegionView;

namespace cvd {

namespace {

void BroadcastFrameBufferChanged(int index) {
  ScreenRegionView::GetInstance()->BroadcastNewFrame(
      static_cast<uint32_t>(index));
}

}  // namespace

BaseComposer::BaseComposer(int64_t vsync_base_timestamp,
                           int32_t vsync_period_ns)
    : vsync_base_timestamp_(vsync_base_timestamp),
      vsync_period_ns_(vsync_period_ns),
      fb_broadcaster_(BroadcastFrameBufferChanged) {
  hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
                reinterpret_cast<const hw_module_t**>(&gralloc_module_));
}

BaseComposer::~BaseComposer() {}

FbBroadcaster BaseComposer::ReplaceFbBroadcaster(FbBroadcaster fb_broadcaster) {
  FbBroadcaster tmp = fb_broadcaster_;
  fb_broadcaster_ = fb_broadcaster;
  return tmp;
}

void BaseComposer::Dump(char* buff __unused, int buff_len __unused) {}

void BaseComposer::Broadcast(int fb_index) {
  fb_broadcaster_(fb_index);
}

int BaseComposer::PostFrameBufferTarget(buffer_handle_t buffer_handle) {
  int fb_index = NextScreenBuffer();
  if (fb_index < 0) {
    ALOGE("Could not get the next buffer. Is the screen region large enough?");
    return -1;
  }
  auto screen_view = ScreenRegionView::GetInstance();
  void* frame_buffer = screen_view->GetBuffer(fb_index);
  const private_handle_t* p_handle =
      reinterpret_cast<const private_handle_t*>(buffer_handle);
  void* buffer;
  int retval = gralloc_module_->lock(gralloc_module_, buffer_handle,
                                     GRALLOC_USAGE_SW_READ_OFTEN, 0, 0,
                                     p_handle->x_res, p_handle->y_res, &buffer);
  if (retval != 0) {
    ALOGE("Got error code %d from lock function", retval);
    return -1;
  }
  memcpy(frame_buffer, buffer, screen_view->buffer_size());
  Broadcast(fb_index);
  return 0;
}  // namespace cvd

int BaseComposer::PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers) {
  // find unsupported overlays
  for (size_t i = 0; i < num_layers; i++) {
    if (IS_TARGET_FRAMEBUFFER(layers[i].compositionType)) {
      continue;
    }
    layers[i].compositionType = HWC_FRAMEBUFFER;
  }
  return 0;
}

int BaseComposer::SetLayers(size_t num_layers, vsoc_hwc_layer* layers) {
  for (size_t idx = 0; idx < num_layers; idx++) {
    if (IS_TARGET_FRAMEBUFFER(layers[idx].compositionType)) {
      return PostFrameBufferTarget(layers[idx].handle);
    }
  }
  return -1;
}

int BaseComposer::NextScreenBuffer() {
  int num_buffers = ScreenRegionView::GetInstance()->number_of_buffers();
  last_frame_buffer_ =
      num_buffers > 0 ? (last_frame_buffer_ + 1) % num_buffers : -1;
  return last_frame_buffer_;
}

}  // namespace cvd