// luma.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import {Geometry} from '../geometry/geometry';
import {unpackIndexedGeometry} from '../geometry/geometry-utils';
import {uid} from '../utils/uid';

export type PlaneGeometryProps = {
  id?: string;
  radius?: number;
  attributes?: any;
};

// Primitives inspired by TDL http://code.google.com/p/webglsamples/,
// copyright 2011 Google Inc. new BSD License
// (http://www.opensource.org/licenses/bsd-license.php).
export class PlaneGeometry extends Geometry {
  constructor(props: PlaneGeometryProps = {}) {
    const {id = uid('plane-geometry')} = props;

    const {indices, attributes} = tesselatePlane(props);
    super({
      ...props,
      id,
      topology: 'triangle-list',
      indices,
      attributes: {...attributes, ...props.attributes}
    });
  }
}

/* eslint-disable complexity, max-statements */
function tesselatePlane(props: any) {
  const {type = 'x,y', offset = 0, flipCull = false, unpack = false} = props;

  const coords = type.split(',');
  // width, height
  let c1len = props[`${coords[0]}len`] || 1;
  const c2len = props[`${coords[1]}len`] || 1;
  // subdivisionsWidth, subdivisionsDepth
  const subdivisions1 = props[`n${coords[0]}`] || 1;
  const subdivisions2 = props[`n${coords[1]}`] || 1;
  const numVertices = (subdivisions1 + 1) * (subdivisions2 + 1);

  const positions = new Float32Array(numVertices * 3);
  const normals = new Float32Array(numVertices * 3);
  const texCoords = new Float32Array(numVertices * 2);

  if (flipCull) {
    c1len = -c1len;
  }

  let i2 = 0;
  let i3 = 0;
  for (let z = 0; z <= subdivisions2; z++) {
    for (let x = 0; x <= subdivisions1; x++) {
      const u = x / subdivisions1;
      const v = z / subdivisions2;
      texCoords[i2 + 0] = flipCull ? 1 - u : u;
      texCoords[i2 + 1] = v;

      switch (type) {
        case 'x,y':
          positions[i3 + 0] = c1len * u - c1len * 0.5;
          positions[i3 + 1] = c2len * v - c2len * 0.5;
          positions[i3 + 2] = offset;

          normals[i3 + 0] = 0;
          normals[i3 + 1] = 0;
          normals[i3 + 2] = flipCull ? 1 : -1;
          break;

        case 'x,z':
          positions[i3 + 0] = c1len * u - c1len * 0.5;
          positions[i3 + 1] = offset;
          positions[i3 + 2] = c2len * v - c2len * 0.5;

          normals[i3 + 0] = 0;
          normals[i3 + 1] = flipCull ? 1 : -1;
          normals[i3 + 2] = 0;
          break;

        case 'y,z':
          positions[i3 + 0] = offset;
          positions[i3 + 1] = c1len * u - c1len * 0.5;
          positions[i3 + 2] = c2len * v - c2len * 0.5;

          normals[i3 + 0] = flipCull ? 1 : -1;
          normals[i3 + 1] = 0;
          normals[i3 + 2] = 0;
          break;

        default:
          throw new Error('PlaneGeometry: unknown type');
      }

      i2 += 2;
      i3 += 3;
    }
  }

  const numVertsAcross = subdivisions1 + 1;
  const indices = new Uint16Array(subdivisions1 * subdivisions2 * 6);

  for (let z = 0; z < subdivisions2; z++) {
    for (let x = 0; x < subdivisions1; x++) {
      const index = (z * subdivisions1 + x) * 6;
      // Make triangle 1 of quad.
      indices[index + 0] = (z + 0) * numVertsAcross + x;
      indices[index + 1] = (z + 1) * numVertsAcross + x;
      indices[index + 2] = (z + 0) * numVertsAcross + x + 1;

      // Make triangle 2 of quad.
      indices[index + 3] = (z + 1) * numVertsAcross + x;
      indices[index + 4] = (z + 1) * numVertsAcross + x + 1;
      indices[index + 5] = (z + 0) * numVertsAcross + x + 1;
    }
  }

  const geometry = {
    indices: {size: 1, value: indices},
    attributes: {
      POSITION: {size: 3, value: positions},
      NORMAL: {size: 3, value: normals},
      TEXCOORD_0: {size: 2, value: texCoords}
    }
  };

  // Optionally, unpack indexed geometry
  return unpack ? unpackIndexedGeometry(geometry) : geometry;
}
