import {
  Vector3,
  Color,
  Vector2,
  Vector4,
  Box3,
  Sphere,
  Matrix4,
  BufferGeometry,
  Matrix,
  Mesh,
  Bone,
  AnimationClip,
  EventDispatcher,
  Object3D,
} from 'three'

/**
 * @deprecated Use Face3 instead.
 */

export interface MorphTarget {
  name: string
  vertices: Vector3[]
}

export interface MorphColor {
  name: string
  colors: Color[]
}

export interface MorphNormals {
  name: string
  normals: Vector3[]
}

/**
 * Base class for geometries
 */
export class Geometry extends EventDispatcher {
  constructor()

  /**
   * Unique number of this geometry instance
   */
  id: number

  uuid: string

  readonly isGeometry: true

  /**
   * Name for this geometry. Default is an empty string.
   * @default ''
   */
  name: string

  /**
   * @default 'Geometry'
   */
  type: string

  /**
   * The array of vertices hold every position of points of the model.
   * To signal an update in this array, Geometry.verticesNeedUpdate needs to be set to true.
   * @default []
   */
  vertices: Vector3[]

  /**
   * Array of vertex colors, matching number and order of vertices.
   * Used in ParticleSystem, Line and Ribbon.
   * Meshes use per-face-use-of-vertex colors embedded directly in faces.
   * To signal an update in this array, Geometry.colorsNeedUpdate needs to be set to true.
   * @default []
   */
  colors: Color[]

  /**
   * Array of triangles or/and quads.
   * The array of faces describe how each vertex in the model is connected with each other.
   * To signal an update in this array, Geometry.elementsNeedUpdate needs to be set to true.
   * @default []
   */
  faces: Face3[]

  /**
   * Array of face UV layers.
   * Each UV layer is an array of UV matching order and number of vertices in faces.
   * To signal an update in this array, Geometry.uvsNeedUpdate needs to be set to true.
   * @default [[]]
   */
  faceVertexUvs: Vector2[][][]

  /**
   * Array of morph targets. Each morph target is a Javascript object:
   *
   * { name: "targetName", vertices: [ new THREE.Vector3(), ... ] }
   *
   * Morph vertices match number and order of primary vertices.
   * @default []
   */
  morphTargets: MorphTarget[]

  /**
   * Array of morph normals. Morph normals have similar structure as morph targets, each normal set is a Javascript object:
   *
   * morphNormal = { name: "NormalName", normals: [ new THREE.Vector3(), ... ] }
   * @default []
   */
  morphNormals: MorphNormals[]

  /**
   * Array of skinning weights, matching number and order of vertices.
   * @default []
   */
  skinWeights: Vector4[]

  /**
   * Array of skinning indices, matching number and order of vertices.
   * @default []
   */
  skinIndices: Vector4[]

  /**
   * @default []
   */
  lineDistances: number[]

  /**
   * Bounding box.
   * @default null
   */
  boundingBox: Box3 | null

  /**
   * Bounding sphere.
   * @default null
   */
  boundingSphere: Sphere | null

  /**
   * Set to true if the vertices array has been updated.
   * @default false
   */
  verticesNeedUpdate: boolean

  /**
   * Set to true if the faces array has been updated.
   * @default false
   */
  elementsNeedUpdate: boolean

  /**
   * Set to true if the uvs array has been updated.
   * @default false
   */
  uvsNeedUpdate: boolean

  /**
   * Set to true if the normals array has been updated.
   * @default false
   */
  normalsNeedUpdate: boolean

  /**
   * Set to true if the colors array has been updated.
   * @default false
   */
  colorsNeedUpdate: boolean

  /**
   * Set to true if the linedistances array has been updated.
   * @default false
   */
  lineDistancesNeedUpdate: boolean

  /**
   *
   * @default false
   */
  groupsNeedUpdate: boolean

  /**
   * Bakes matrix transform directly into vertex coordinates.
   */
  applyMatrix4(matrix: Matrix4): Geometry

  rotateX(angle: number): Geometry
  rotateY(angle: number): Geometry
  rotateZ(angle: number): Geometry

  translate(x: number, y: number, z: number): Geometry
  scale(x: number, y: number, z: number): Geometry
  lookAt(vector: Vector3): void

  fromBufferGeometry(geometry: BufferGeometry): Geometry

  center(): Geometry

  normalize(): Geometry

  /**
   * Computes face normals.
   */
  computeFaceNormals(): void

  /**
   * Computes vertex normals by averaging face normals.
   * Face normals must be existing / computed beforehand.
   */
  computeVertexNormals(areaWeighted?: boolean): void

  /**
   * Compute vertex normals, but duplicating face normals.
   */
  computeFlatVertexNormals(): void

  /**
   * Computes morph normals.
   */
  computeMorphNormals(): void

  /**
   * Computes bounding box of the geometry, updating {@link Geometry.boundingBox} attribute.
   */
  computeBoundingBox(): void

  /**
   * Computes bounding sphere of the geometry, updating Geometry.boundingSphere attribute.
   * Neither bounding boxes or bounding spheres are computed by default. They need to be explicitly computed, otherwise they are null.
   */
  computeBoundingSphere(): void

  merge(geometry: Geometry, matrix?: Matrix, materialIndexOffset?: number): void

  mergeMesh(mesh: Mesh): void

  /**
   * Checks for duplicate vertices using hashmap for specified number of decimal points, e.g. 4 for epsilon of 0.0001
   * Duplicated vertices are removed and faces' vertices are updated.
   */
  mergeVertices(precisionPoints?: number): number

  setFromPoints(points: Vector2[] | Vector3[]): this

  sortFacesByMaterialIndex(): void

  toBufferGeometry(): BufferGeometry

  static createBufferGeometryFromObject(object: Object3D): BufferGeometry

  toJSON(): any

  /**
   * Creates a new clone of the Geometry.
   */
  clone(): Geometry

  copy(source: Geometry): this

  /**
   * Removes The object from memory.
   * Don't forget to call this method when you remove an geometry because it can cuase meomory leaks.
   */
  dispose(): void

  // These properties do not exist in a normal Geometry class, but if you use the instance that was passed by JSONLoader, it will be added.
  bones: Bone[]
  animation: AnimationClip
  animations: AnimationClip[]
}

/**
 * Triangle face.
 */
export class Face3 {
  /**
   * @param a Vertex A index.
   * @param b Vertex B index.
   * @param c Vertex C index.
   * @param normal Face normal or array of vertex normals.
   * @param color Face color or array of vertex colors.
   * @param materialIndex Material index.
   */
  constructor(
    a: number,
    b: number,
    c: number,
    normal?: Vector3 | Vector3[],
    color?: Color | Color[],
    materialIndex?: number,
  )

  /**
   * Vertex A index.
   */
  a: number

  /**
   * Vertex B index.
   */
  b: number

  /**
   * Vertex C index.
   */
  c: number

  /**
   * Face normal.
   * @default new THREE.Vector3()
   */
  normal: Vector3

  /**
   * Array of 3 vertex normals.
   * @default []
   */
  vertexNormals: Vector3[]

  /**
   * Face color.
   * @default new THREE.Color()
   */
  color: Color

  /**
   * Array of 3 vertex colors.
   * @default []
   */
  vertexColors: Color[]

  /**
   * Material index (points to {@link Mesh.material}).
   * @default 0
   */
  materialIndex: number

  clone(): this
  copy(source: Face3): this
}
