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

#include <vector>

#include "flutter/impeller/entity/geometry/superellipse_geometry.h"

#include "impeller/geometry/constants.h"

namespace impeller {

SuperellipseGeometry::SuperellipseGeometry(const Point& center,
                                           Scalar radius,
                                           Scalar degree,
                                           Scalar alpha,
                                           Scalar beta)
    : center_(center),
      degree_(degree),
      radius_(radius),
      alpha_(alpha),
      beta_(beta) {}

SuperellipseGeometry::~SuperellipseGeometry() {}

GeometryResult SuperellipseGeometry::GetPositionBuffer(
    const ContentContext& renderer,
    const Entity& entity,
    RenderPass& pass) const {
  // https://math.stackexchange.com/questions/2573746/superellipse-parametric-equation
  Scalar a = alpha_;
  Scalar b = beta_;
  Scalar n = degree_;

  // TODO(jonahwilliams): determine parameter values based on scaling factor.
  Scalar step = kPi / 80;

  // Generate the points for the top left quadrant, and then mirror to the other
  // quadrants.
  std::vector<Point> points;
  points.reserve(41);
  for (int i = 0; i <= 40; i++) {
    Scalar t = i * step;
    Scalar x = a * pow(abs(cos(t)), 2 / n);
    Scalar y = b * pow(abs(sin(t)), 2 / n);
    points.emplace_back(x * radius_, y * radius_);
  }

  static constexpr Point reflection[4] = {{1, 1}, {-1, 1}, {-1, -1}, {1, -1}};

  // Reflect into the 4 quadrants and generate the tessellated mesh. The
  // iteration order is reversed so that the trianges are continuous from
  // quadrant to quadrant.
  std::vector<Point> geometry;
  geometry.reserve(1 + 4 * points.size());
  geometry.push_back(center_);
  for (auto i = 0u; i < points.size(); i++) {
    geometry.push_back(center_ + (reflection[0] * points[i]));
  }
  for (auto i = 0u; i < points.size(); i++) {
    geometry.push_back(center_ +
                       (reflection[1] * points[points.size() - i - 1]));
  }
  for (auto i = 0u; i < points.size(); i++) {
    geometry.push_back(center_ + (reflection[2] * points[i]));
  }
  for (auto i = 0u; i < points.size(); i++) {
    geometry.push_back(center_ +
                       (reflection[3] * points[points.size() - i - 1]));
  }

  std::vector<uint16_t> indices;
  indices.reserve(geometry.size() * 3);
  for (auto i = 2u; i < geometry.size(); i++) {
    indices.push_back(0);
    indices.push_back(i - 1);
    indices.push_back(i);
  }

  auto& data_host_buffer = renderer.GetTransientsDataBuffer();
  auto& indexes_host_buffer = renderer.GetTransientsIndexesBuffer();
  return GeometryResult{
      .type = PrimitiveType::kTriangle,
      .vertex_buffer =
          {
              .vertex_buffer = data_host_buffer.Emplace(
                  geometry.data(), geometry.size() * sizeof(Point),
                  alignof(Point)),
              .index_buffer = indexes_host_buffer.Emplace(
                  indices.data(), indices.size() * sizeof(uint16_t),
                  alignof(uint16_t)),
              .vertex_count = indices.size(),
              .index_type = IndexType::k16bit,
          },
      .transform = entity.GetShaderTransform(pass),
  };
}

std::optional<Rect> SuperellipseGeometry::GetCoverage(
    const Matrix& transform) const {
  return Rect::MakeOriginSize(center_ - Point(radius_, radius_),
                              Size(radius_ * 2, radius_ * 2));
}

bool SuperellipseGeometry::CoversArea(const Matrix& transform,
                                      const Rect& rect) const {
  return false;
}

bool SuperellipseGeometry::IsAxisAlignedRect() const {
  return false;
}

}  // namespace impeller
