// Copyright (c) 2015, Google Inc. Please see the AUTHORS file for details.
// All rights reserved. Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

part of '../../../vector_math_geometry.dart';

class GeometryGeneratorFlags {
  final bool texCoords;
  final bool normals;
  final bool tangents;

  GeometryGeneratorFlags(
      {this.texCoords = true, this.normals = true, this.tangents = true});
}

abstract class GeometryGenerator {
  int get vertexCount;
  int get indexCount;

  MeshGeometry createGeometry(
      {GeometryGeneratorFlags? flags, List<GeometryFilter>? filters}) {
    flags ??= GeometryGeneratorFlags();

    VertexAttrib positionAttrib;
    VertexAttrib texCoordAttrib;
    VertexAttrib normalAttrib;
    VertexAttrib tangentAttrib;

    Vector2List? texCoordView;
    Vector3List? positionView;
    Vector3List? normalView;
    Vector4List tangentView;

    final attribs = <VertexAttrib>[];

    positionAttrib = VertexAttrib('POSITION', 3, 'float');
    attribs.add(positionAttrib);

    if (flags.texCoords || flags.tangents) {
      texCoordAttrib = VertexAttrib('TEXCOORD0', 2, 'float');
      attribs.add(texCoordAttrib);
    }

    if (flags.normals || flags.tangents) {
      normalAttrib = VertexAttrib('NORMAL', 3, 'float');
      attribs.add(normalAttrib);
    }

    if (flags.tangents) {
      tangentAttrib = VertexAttrib('TANGENT', 4, 'float');
      attribs.add(tangentAttrib);
    }

    var mesh = MeshGeometry(vertexCount, attribs)
      ..indices = Uint16List(indexCount);
    generateIndices(mesh.indices!);

    var view = mesh.getViewForAttrib('POSITION');
    if (view is Vector3List) {
      positionView = view;
      generateVertexPositions(positionView, mesh.indices!);
    }

    if (flags.texCoords || flags.tangents) {
      view = mesh.getViewForAttrib('TEXCOORD0');
      if (view is Vector2List) {
        texCoordView = view;
        generateVertexTexCoords(texCoordView, positionView!, mesh.indices!);
      }
    }

    if (flags.normals || flags.tangents) {
      view = mesh.getViewForAttrib('NORMAL');
      if (view is Vector3List) {
        normalView = view;
        generateVertexNormals(normalView, positionView!, mesh.indices!);
      }
    }

    if (flags.tangents) {
      view = mesh.getViewForAttrib('TANGENT');
      if (view is Vector4List) {
        tangentView = view;
        generateVertexTangents(tangentView, positionView!, normalView!,
            texCoordView!, mesh.indices!);
      }
    }

    if (filters != null) {
      for (var filter in filters) {
        if (filter.inplace && filter is InplaceGeometryFilter) {
          filter.filterInplace(mesh);
        } else {
          mesh = filter.filter(mesh);
        }
      }
    }

    return mesh;
  }

  void generateIndices(Uint16List indices);

  void generateVertexPositions(Vector3List positions, Uint16List indices);

  void generateVertexTexCoords(
      Vector2List texCoords, Vector3List positions, Uint16List indices) {
    for (var i = 0; i < positions.length; ++i) {
      final p = positions[i];

      // These are TERRIBLE texture coords, but it's better than nothing.
      // Override this function and put better ones in place!
      texCoords[i] = Vector2(p.x + p.z, p.y + p.z);
    }
  }

  void generateVertexNormals(
      Vector3List normals, Vector3List positions, Uint16List indices) {
    generateNormals(normals, positions, indices);
  }

  void generateVertexTangents(Vector4List tangents, Vector3List positions,
      Vector3List normals, Vector2List texCoords, Uint16List indices) {
    generateTangents(tangents, positions, normals, texCoords, indices);
  }
}
