// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#pragma once

#include <optional>

#include "flutter/fml/logging.h"
#include "flutter/fml/macros.h"
#include "impeller/core/formats.h"
#include "impeller/core/shader_types.h"
#include "impeller/renderer/backend/gles/gles.h"

namespace impeller {

constexpr GLenum ToMode(PrimitiveType primitive_type) {
  switch (primitive_type) {
    case PrimitiveType::kTriangle:
      return GL_TRIANGLES;
    case PrimitiveType::kTriangleStrip:
      return GL_TRIANGLE_STRIP;
    case PrimitiveType::kLine:
      return GL_LINES;
    case PrimitiveType::kLineStrip:
      return GL_LINE_STRIP;
    case PrimitiveType::kPoint:
      return GL_POINTS;
  }
  FML_UNREACHABLE();
}

constexpr GLenum ToIndexType(IndexType type) {
  switch (type) {
    case IndexType::kUnknown:
    case IndexType::kNone:
      FML_UNREACHABLE();
    case IndexType::k16bit:
      return GL_UNSIGNED_SHORT;
    case IndexType::k32bit:
      return GL_UNSIGNED_INT;
  }
  FML_UNREACHABLE();
}

constexpr GLenum ToStencilOp(StencilOperation op) {
  switch (op) {
    case StencilOperation::kKeep:
      return GL_KEEP;
    case StencilOperation::kZero:
      return GL_ZERO;
    case StencilOperation::kSetToReferenceValue:
      return GL_REPLACE;
    case StencilOperation::kIncrementClamp:
      return GL_INCR;
    case StencilOperation::kDecrementClamp:
      return GL_DECR;
    case StencilOperation::kInvert:
      return GL_INVERT;
    case StencilOperation::kIncrementWrap:
      return GL_INCR_WRAP;
    case StencilOperation::kDecrementWrap:
      return GL_DECR_WRAP;
  }
  FML_UNREACHABLE();
}

constexpr GLenum ToCompareFunction(CompareFunction func) {
  switch (func) {
    case CompareFunction::kNever:
      return GL_NEVER;
    case CompareFunction::kAlways:
      return GL_ALWAYS;
    case CompareFunction::kLess:
      return GL_LESS;
    case CompareFunction::kEqual:
      return GL_EQUAL;
    case CompareFunction::kLessEqual:
      return GL_LEQUAL;
    case CompareFunction::kGreater:
      return GL_GREATER;
    case CompareFunction::kNotEqual:
      return GL_NOTEQUAL;
    case CompareFunction::kGreaterEqual:
      return GL_GEQUAL;
  }
  FML_UNREACHABLE();
}

constexpr GLenum ToBlendFactor(BlendFactor factor) {
  switch (factor) {
    case BlendFactor::kZero:
      return GL_ZERO;
    case BlendFactor::kOne:
      return GL_ONE;
    case BlendFactor::kSourceColor:
      return GL_SRC_COLOR;
    case BlendFactor::kOneMinusSourceColor:
      return GL_ONE_MINUS_SRC_COLOR;
    case BlendFactor::kSourceAlpha:
      return GL_SRC_ALPHA;
    case BlendFactor::kOneMinusSourceAlpha:
      return GL_ONE_MINUS_SRC_ALPHA;
    case BlendFactor::kDestinationColor:
      return GL_DST_COLOR;
    case BlendFactor::kOneMinusDestinationColor:
      return GL_ONE_MINUS_DST_COLOR;
    case BlendFactor::kDestinationAlpha:
      return GL_DST_ALPHA;
    case BlendFactor::kOneMinusDestinationAlpha:
      return GL_ONE_MINUS_DST_ALPHA;
    case BlendFactor::kSourceAlphaSaturated:
      return GL_SRC_ALPHA_SATURATE;
    case BlendFactor::kBlendColor:
      return GL_CONSTANT_COLOR;
    case BlendFactor::kOneMinusBlendColor:
      return GL_ONE_MINUS_CONSTANT_COLOR;
    case BlendFactor::kBlendAlpha:
      return GL_CONSTANT_ALPHA;
    case BlendFactor::kOneMinusBlendAlpha:
      return GL_ONE_MINUS_CONSTANT_ALPHA;
  }
  FML_UNREACHABLE();
}

constexpr GLenum ToBlendOperation(BlendOperation op) {
  switch (op) {
    case BlendOperation::kAdd:
      return GL_FUNC_ADD;
    case BlendOperation::kSubtract:
      return GL_FUNC_SUBTRACT;
    case BlendOperation::kReverseSubtract:
      return GL_FUNC_REVERSE_SUBTRACT;
  }
  FML_UNREACHABLE();
}

constexpr std::optional<GLenum> ToVertexAttribType(ShaderType type) {
  switch (type) {
    case ShaderType::kSignedByte:
      return GL_BYTE;
    case ShaderType::kUnsignedByte:
      return GL_UNSIGNED_BYTE;
    case ShaderType::kSignedShort:
      return GL_SHORT;
    case ShaderType::kUnsignedShort:
      return GL_UNSIGNED_SHORT;
    case ShaderType::kFloat:
      return GL_FLOAT;
    case ShaderType::kUnknown:
    case ShaderType::kVoid:
    case ShaderType::kBoolean:
    case ShaderType::kSignedInt:
    case ShaderType::kUnsignedInt:
    case ShaderType::kSignedInt64:
    case ShaderType::kUnsignedInt64:
    case ShaderType::kAtomicCounter:
    case ShaderType::kHalfFloat:
    case ShaderType::kDouble:
    case ShaderType::kStruct:
    case ShaderType::kImage:
    case ShaderType::kSampledImage:
    case ShaderType::kSampler:
      return std::nullopt;
  }
  FML_UNREACHABLE();
}

constexpr GLenum ToTextureType(TextureType type) {
  switch (type) {
    case TextureType::kTexture2D:
      return GL_TEXTURE_2D;
    case TextureType::kTexture2DMultisample:
      return GL_TEXTURE_2D_MULTISAMPLE;
    case TextureType::kTextureCube:
      return GL_TEXTURE_CUBE_MAP;
    case TextureType::kTextureExternalOES:
      return GL_TEXTURE_EXTERNAL_OES;
  }
  FML_UNREACHABLE();
}

constexpr std::optional<GLenum> ToTextureTarget(TextureType type) {
  switch (type) {
    case TextureType::kTexture2D:
      return GL_TEXTURE_2D;
    case TextureType::kTexture2DMultisample:
      return std::nullopt;
    case TextureType::kTextureCube:
      return GL_TEXTURE_CUBE_MAP;
    case TextureType::kTextureExternalOES:
      return GL_TEXTURE_EXTERNAL_OES;
  }
  FML_UNREACHABLE();
}

}  // namespace impeller
