/* SPDX-FileCopyrightText: 2017 Blender Authors. All rights reserved.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later */

/** \file
 * \ingroup gpu
 *
 * Glue to make the new Select-Next engine work with the old GPU select API.
 */
#include <cfloat>

#include "BLI_rect.h"
#include "BLI_span.hh"

#include "gpu_select_private.hh"

namespace blender {

struct GPUSelectNextState {
  /** Result buffer set on initialization. */
  GPUSelectBuffer *buffer = nullptr;
  /** Area of the viewport to render / select from. */
  rcti rect = {0, 0, 0, 0};
  /** The radius relative to the center of `rect` in which to select. Ignored if 0. */
  int radius = 0;
  /** Number of hits. Set to -1 if it overflows buffer_len. */
  uint hits = -1;
  /** Mode of operation. */
  GPUSelectMode mode = GPUSelectMode::GPU_SELECT_INVALID;
};

static GPUSelectNextState g_state = {};

void gpu_select_next_begin(GPUSelectBuffer *buffer,
                           const rcti *input,
                           const int radius,
                           GPUSelectMode mode)

{
  g_state.buffer = buffer;
  g_state.rect = *input;
  g_state.mode = mode;
  g_state.radius = radius;
}

int2 gpu_select_next_get_pick_area_center()
{
  return int2(BLI_rcti_size_x(&g_state.rect) / 2, BLI_rcti_size_y(&g_state.rect) / 2);
}

GPUSelectMode gpu_select_next_get_mode()
{
  return g_state.mode;
}

int gpu_select_next_get_radius()
{
  return g_state.radius;
}

void gpu_select_next_set_result(GPUSelectResult *hit_buf, uint hit_len)
{
  const int old_size = g_state.buffer->storage.size();
  g_state.buffer->storage.resize(old_size + hit_len);
  MutableSpan<GPUSelectResult> hit_results = g_state.buffer->storage.as_mutable_span().slice(
      old_size, hit_len);
  const Span<GPUSelectResult> hits(hit_buf, hit_len);

  /* TODO(fclem): There might be some conversion to do to align to the other APIs output. */
  switch (g_state.mode) {
    case GPUSelectMode::GPU_SELECT_ALL:
      hit_results.copy_from(hits);
      break;
    case GPUSelectMode::GPU_SELECT_PICK_ALL:
      hit_results.copy_from(hits);
      break;
    case GPUSelectMode::GPU_SELECT_PICK_NEAREST:
      hit_results.copy_from(hits);
      break;
    case GPUSelectMode::GPU_SELECT_NEAREST_FIRST_PASS:
    case GPUSelectMode::GPU_SELECT_NEAREST_SECOND_PASS:
    case GPUSelectMode::GPU_SELECT_INVALID:
      BLI_assert_unreachable();
      break;
  }

  g_state.hits = hit_len;
}

uint gpu_select_next_end()
{
  return g_state.hits;
}

}  // namespace blender
