/* SPDX-FileCopyrightText: 2015 Blender Foundation
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * Author: Sergey Sharybin. */

#include "opensubdiv_evaluator_capi.hh"

#include <opensubdiv/osd/glslPatchShaderSource.h>

#include "MEM_guardedalloc.h"
#include <new>

#include "internal/evaluator/evaluator_cache_impl.h"
#include "internal/evaluator/evaluator_impl.h"

static void setSettings(struct OpenSubdiv_Evaluator *evaluator,
                        const OpenSubdiv_EvaluatorSettings *settings)
{
  evaluator->impl->eval_output->setSettings(settings);
}

static void setCoarsePositions(OpenSubdiv_Evaluator *evaluator,
                               const float *positions,
                               const int start_vertex_index,
                               const int num_vertices)
{
  evaluator->impl->eval_output->setCoarsePositions(positions, start_vertex_index, num_vertices);
}

static void setVertexData(OpenSubdiv_Evaluator *evaluator,
                          const float *vertex_data,
                          const int start_vertex_index,
                          const int num_vertices)
{
  evaluator->impl->eval_output->setVertexData(vertex_data, start_vertex_index, num_vertices);
}

static void setVaryingData(OpenSubdiv_Evaluator *evaluator,
                           const float *varying_data,
                           const int start_vertex_index,
                           const int num_vertices)
{
  evaluator->impl->eval_output->setVaryingData(varying_data, start_vertex_index, num_vertices);
}

static void setFaceVaryingData(OpenSubdiv_Evaluator *evaluator,
                               const int face_varying_channel,
                               const float *face_varying_data,
                               const int start_vertex_index,
                               const int num_vertices)
{
  evaluator->impl->eval_output->setFaceVaryingData(
      face_varying_channel, face_varying_data, start_vertex_index, num_vertices);
}

static void setCoarsePositionsFromBuffer(OpenSubdiv_Evaluator *evaluator,
                                         const void *buffer,
                                         const int start_offset,
                                         const int stride,
                                         const int start_vertex_index,
                                         const int num_vertices)
{
  evaluator->impl->eval_output->setCoarsePositionsFromBuffer(
      buffer, start_offset, stride, start_vertex_index, num_vertices);
}

static void setVaryingDataFromBuffer(OpenSubdiv_Evaluator *evaluator,
                                     const void *buffer,
                                     const int start_offset,
                                     const int stride,
                                     const int start_vertex_index,
                                     const int num_vertices)
{
  evaluator->impl->eval_output->setVaryingDataFromBuffer(
      buffer, start_offset, stride, start_vertex_index, num_vertices);
}

static void setFaceVaryingDataFromBuffer(OpenSubdiv_Evaluator *evaluator,
                                         const int face_varying_channel,
                                         const void *buffer,
                                         const int start_offset,
                                         const int stride,
                                         const int start_vertex_index,
                                         const int num_vertices)
{
  evaluator->impl->eval_output->setFaceVaryingDataFromBuffer(
      face_varying_channel, buffer, start_offset, stride, start_vertex_index, num_vertices);
}

static void refine(OpenSubdiv_Evaluator *evaluator)
{
  evaluator->impl->eval_output->refine();
}

static void evaluateLimit(OpenSubdiv_Evaluator *evaluator,
                          const int ptex_face_index,
                          const float face_u,
                          const float face_v,
                          float P[3],
                          float dPdu[3],
                          float dPdv[3])
{
  evaluator->impl->eval_output->evaluateLimit(ptex_face_index, face_u, face_v, P, dPdu, dPdv);
}

static void evaluatePatchesLimit(OpenSubdiv_Evaluator *evaluator,
                                 const OpenSubdiv_PatchCoord *patch_coords,
                                 const int num_patch_coords,
                                 float *P,
                                 float *dPdu,
                                 float *dPdv)
{
  evaluator->impl->eval_output->evaluatePatchesLimit(
      patch_coords, num_patch_coords, P, dPdu, dPdv);
}

static void evaluateVertexData(OpenSubdiv_Evaluator *evaluator,
                               const int ptex_face_index,
                               float face_u,
                               float face_v,
                               float vertex_data[3])
{
  evaluator->impl->eval_output->evaluateVertexData(ptex_face_index, face_u, face_v, vertex_data);
}

static void evaluateVarying(OpenSubdiv_Evaluator *evaluator,
                            const int ptex_face_index,
                            float face_u,
                            float face_v,
                            float varying[3])
{
  evaluator->impl->eval_output->evaluateVarying(ptex_face_index, face_u, face_v, varying);
}

static void evaluateFaceVarying(OpenSubdiv_Evaluator *evaluator,
                                const int face_varying_channel,
                                const int ptex_face_index,
                                float face_u,
                                float face_v,
                                float face_varying[2])
{
  evaluator->impl->eval_output->evaluateFaceVarying(
      face_varying_channel, ptex_face_index, face_u, face_v, face_varying);
}

static void getPatchMap(struct OpenSubdiv_Evaluator *evaluator,
                        struct OpenSubdiv_Buffer *patch_map_handles,
                        struct OpenSubdiv_Buffer *patch_map_quadtree,
                        int *min_patch_face,
                        int *max_patch_face,
                        int *max_depth,
                        int *patches_are_triangular)
{
  evaluator->impl->eval_output->getPatchMap(patch_map_handles,
                                            patch_map_quadtree,
                                            min_patch_face,
                                            max_patch_face,
                                            max_depth,
                                            patches_are_triangular);
}

static void fillPatchArraysBuffer(struct OpenSubdiv_Evaluator *evaluator,
                                  struct OpenSubdiv_Buffer *patch_array_buffer)
{
  evaluator->impl->eval_output->fillPatchArraysBuffer(patch_array_buffer);
}

static void wrapPatchIndexBuffer(struct OpenSubdiv_Evaluator *evaluator,
                                 struct OpenSubdiv_Buffer *patch_index_buffer)
{
  evaluator->impl->eval_output->wrapPatchIndexBuffer(patch_index_buffer);
}

static void wrapPatchParamBuffer(struct OpenSubdiv_Evaluator *evaluator,
                                 struct OpenSubdiv_Buffer *patch_param_buffer)
{
  evaluator->impl->eval_output->wrapPatchParamBuffer(patch_param_buffer);
}

static void wrapSrcBuffer(struct OpenSubdiv_Evaluator *evaluator,
                          struct OpenSubdiv_Buffer *src_buffer)
{
  evaluator->impl->eval_output->wrapSrcBuffer(src_buffer);
}

static void wrapSrcVertexDataBuffer(struct OpenSubdiv_Evaluator *evaluator,
                                    struct OpenSubdiv_Buffer *src_buffer)
{
  evaluator->impl->eval_output->wrapSrcVertexDataBuffer(src_buffer);
}

static void fillFVarPatchArraysBuffer(struct OpenSubdiv_Evaluator *evaluator,
                                      const int face_varying_channel,
                                      struct OpenSubdiv_Buffer *patch_array_buffer)
{
  evaluator->impl->eval_output->fillFVarPatchArraysBuffer(face_varying_channel,
                                                          patch_array_buffer);
}

static void wrapFVarPatchIndexBuffer(struct OpenSubdiv_Evaluator *evaluator,
                                     const int face_varying_channel,
                                     struct OpenSubdiv_Buffer *patch_index_buffer)
{
  evaluator->impl->eval_output->wrapFVarPatchIndexBuffer(face_varying_channel, patch_index_buffer);
}

static void wrapFVarPatchParamBuffer(struct OpenSubdiv_Evaluator *evaluator,
                                     const int face_varying_channel,
                                     struct OpenSubdiv_Buffer *patch_param_buffer)
{
  evaluator->impl->eval_output->wrapFVarPatchParamBuffer(face_varying_channel, patch_param_buffer);
}

static void wrapFVarSrcBuffer(struct OpenSubdiv_Evaluator *evaluator,
                              const int face_varying_channel,
                              struct OpenSubdiv_Buffer *src_buffer)
{
  evaluator->impl->eval_output->wrapFVarSrcBuffer(face_varying_channel, src_buffer);
}

static bool hasVertexData(struct OpenSubdiv_Evaluator *evaluator)
{
  return evaluator->impl->eval_output->hasVertexData();
}

static void assignFunctionPointers(OpenSubdiv_Evaluator *evaluator)
{
  evaluator->setSettings = setSettings;

  evaluator->setCoarsePositions = setCoarsePositions;
  evaluator->setVertexData = setVertexData;
  evaluator->setVaryingData = setVaryingData;
  evaluator->setFaceVaryingData = setFaceVaryingData;

  evaluator->setCoarsePositionsFromBuffer = setCoarsePositionsFromBuffer;
  evaluator->setVaryingDataFromBuffer = setVaryingDataFromBuffer;
  evaluator->setFaceVaryingDataFromBuffer = setFaceVaryingDataFromBuffer;

  evaluator->refine = refine;

  evaluator->evaluateLimit = evaluateLimit;
  evaluator->evaluateVarying = evaluateVarying;
  evaluator->evaluateVertexData = evaluateVertexData;
  evaluator->evaluateFaceVarying = evaluateFaceVarying;

  evaluator->evaluatePatchesLimit = evaluatePatchesLimit;

  evaluator->getPatchMap = getPatchMap;

  evaluator->fillPatchArraysBuffer = fillPatchArraysBuffer;
  evaluator->wrapPatchIndexBuffer = wrapPatchIndexBuffer;
  evaluator->wrapPatchParamBuffer = wrapPatchParamBuffer;
  evaluator->wrapSrcBuffer = wrapSrcBuffer;
  evaluator->wrapSrcVertexDataBuffer = wrapSrcVertexDataBuffer;

  evaluator->fillFVarPatchArraysBuffer = fillFVarPatchArraysBuffer;
  evaluator->wrapFVarPatchIndexBuffer = wrapFVarPatchIndexBuffer;
  evaluator->wrapFVarPatchParamBuffer = wrapFVarPatchParamBuffer;
  evaluator->wrapFVarSrcBuffer = wrapFVarSrcBuffer;

  evaluator->hasVertexData = hasVertexData;
}

OpenSubdiv_Evaluator *openSubdiv_createEvaluatorFromTopologyRefiner(
    OpenSubdiv_TopologyRefiner *topology_refiner,
    eOpenSubdivEvaluator evaluator_type,
    OpenSubdiv_EvaluatorCache *evaluator_cache)
{
  OpenSubdiv_Evaluator *evaluator = MEM_new<OpenSubdiv_Evaluator>(__func__);
  assignFunctionPointers(evaluator);
  evaluator->impl = openSubdiv_createEvaluatorInternal(
      topology_refiner, evaluator_type, evaluator_cache ? evaluator_cache->impl : nullptr);
  evaluator->type = evaluator->impl ? evaluator_type : static_cast<eOpenSubdivEvaluator>(0);
  return evaluator;
}

void openSubdiv_deleteEvaluator(OpenSubdiv_Evaluator *evaluator)
{
  openSubdiv_deleteEvaluatorInternal(evaluator->impl);
  MEM_delete(evaluator);
}

OpenSubdiv_EvaluatorCache *openSubdiv_createEvaluatorCache(eOpenSubdivEvaluator evaluator_type)
{
  OpenSubdiv_EvaluatorCache *evaluator_cache = MEM_new<OpenSubdiv_EvaluatorCache>(__func__);
  evaluator_cache->impl = openSubdiv_createEvaluatorCacheInternal(evaluator_type);
  return evaluator_cache;
}

void openSubdiv_deleteEvaluatorCache(OpenSubdiv_EvaluatorCache *evaluator_cache)
{
  if (!evaluator_cache) {
    return;
  }

  openSubdiv_deleteEvaluatorCacheInternal(evaluator_cache->impl);
  MEM_delete(evaluator_cache);
}

const char *openSubdiv_getGLSLPatchBasisSource(void)
{
  /* Using a global string to avoid dealing with memory allocation/ownership. */
  static std::string patch_basis_source;
  if (patch_basis_source.empty()) {
    patch_basis_source = OpenSubdiv::Osd::GLSLPatchShaderSource::GetPatchBasisShaderSource();
  }
  return patch_basis_source.c_str();
}
