// 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 "impeller/entity/geometry/geometry.h"

#include <memory>
#include <optional>

#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/pipelines.h"
#include "impeller/entity/geometry/arc_geometry.h"
#include "impeller/entity/geometry/circle_geometry.h"
#include "impeller/entity/geometry/cover_geometry.h"
#include "impeller/entity/geometry/ellipse_geometry.h"
#include "impeller/entity/geometry/fill_path_geometry.h"
#include "impeller/entity/geometry/line_geometry.h"
#include "impeller/entity/geometry/rect_geometry.h"
#include "impeller/entity/geometry/round_rect_geometry.h"
#include "impeller/entity/geometry/round_superellipse_geometry.h"
#include "impeller/entity/geometry/stroke_path_geometry.h"
#include "impeller/geometry/rect.h"

namespace impeller {

GeometryResult Geometry::ComputePositionGeometry(
    const ContentContext& renderer,
    const Tessellator::VertexGenerator& generator,
    const Entity& entity,
    RenderPass& pass) {
  using VT = SolidFillVertexShader::PerVertexData;

  size_t count = generator.GetVertexCount();

  return GeometryResult{
      .type = generator.GetTriangleType(),
      .vertex_buffer =
          {
              .vertex_buffer = renderer.GetTransientsDataBuffer().Emplace(
                  count * sizeof(VT), alignof(VT),
                  [&generator](uint8_t* buffer) {
                    auto vertices = reinterpret_cast<VT*>(buffer);
                    generator.GenerateVertices([&vertices](const Point& p) {
                      *vertices++ = {
                          .position = p,
                      };
                    });
                    FML_DCHECK(vertices == reinterpret_cast<VT*>(buffer) +
                                               generator.GetVertexCount());
                  }),
              .vertex_count = count,
              .index_type = IndexType::kNone,
          },
      .transform = entity.GetShaderTransform(pass),
  };
}

GeometryResult::Mode Geometry::GetResultMode() const {
  return GeometryResult::Mode::kNormal;
}

std::unique_ptr<Geometry> Geometry::MakeFillPath(
    const flutter::DlPath& path,
    std::optional<Rect> inner_rect) {
  return std::make_unique<FillPathGeometry>(path, inner_rect);
}

std::unique_ptr<Geometry> Geometry::MakeStrokePath(
    const flutter::DlPath& path,
    const StrokeParameters& stroke) {
  // Skia behaves like this.
  StrokeParameters parameters = stroke;
  if (parameters.miter_limit < 0) {
    parameters.miter_limit = 4.0;
  }
  return std::make_unique<StrokePathGeometry>(path, parameters);
}

std::unique_ptr<Geometry> Geometry::MakeCover() {
  return std::make_unique<CoverGeometry>();
}

std::unique_ptr<Geometry> Geometry::MakeRect(const Rect& rect) {
  return std::make_unique<FillRectGeometry>(rect);
}

std::unique_ptr<Geometry> Geometry::MakeOval(const Rect& rect) {
  return std::make_unique<EllipseGeometry>(rect);
}

std::unique_ptr<Geometry> Geometry::MakeLine(const Point& p0,
                                             const Point& p1,
                                             const StrokeParameters& stroke) {
  return std::make_unique<LineGeometry>(p0, p1, stroke);
}

std::unique_ptr<Geometry> Geometry::MakeCircle(const Point& center,
                                               Scalar radius) {
  return std::make_unique<CircleGeometry>(center, radius);
}

std::unique_ptr<Geometry> Geometry::MakeStrokedCircle(const Point& center,
                                                      Scalar radius,
                                                      Scalar stroke_width) {
  return std::make_unique<CircleGeometry>(center, radius, stroke_width);
}

std::unique_ptr<Geometry> Geometry::MakeFilledArc(const Rect& oval_bounds,
                                                  Degrees start,
                                                  Degrees sweep,
                                                  bool include_center) {
  return std::make_unique<ArcGeometry>(
      Arc(oval_bounds, start, sweep, include_center));
}

std::unique_ptr<Geometry> Geometry::MakeStrokedArc(
    const Rect& oval_bounds,
    Degrees start,
    Degrees sweep,
    const StrokeParameters& stroke) {
  return std::make_unique<ArcGeometry>(Arc(oval_bounds, start, sweep, false),
                                       stroke);
}

std::unique_ptr<Geometry> Geometry::MakeRoundRect(const Rect& rect,
                                                  const Size& radii) {
  return std::make_unique<RoundRectGeometry>(rect, radii);
}

std::unique_ptr<Geometry> Geometry::MakeRoundSuperellipse(
    const Rect& rect,
    Scalar corner_radius) {
  return std::make_unique<RoundSuperellipseGeometry>(rect, corner_radius);
}

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

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

bool Geometry::CanApplyMaskFilter() const {
  return true;
}

// static
Scalar Geometry::ComputeStrokeAlphaCoverage(const Matrix& transform,
                                            Scalar stroke_width) {
  Scalar scaled_stroke_width = transform.GetMaxBasisLengthXY() * stroke_width;
  if (scaled_stroke_width == 0.0 || scaled_stroke_width >= kMinStrokeSize) {
    return 1.0;
  }
  // This scalling is eyeballed from Skia.
  return std::clamp(scaled_stroke_width * 2.0f, 0.f, 1.f);
}

}  // namespace impeller
