var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

// Copyright (c) 2015 - 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import * as Polygon from './polygon';
import { experimental } from '../../core';
var fp64LowPart = experimental.fp64LowPart,
    fillArray = experimental.fillArray;

import earcut from 'earcut';

function getPickingColor(index) {
  return [index + 1 & 255, index + 1 >> 8 & 255, index + 1 >> 8 >> 8 & 255];
}

function arrayPush(array, values) {
  var length = values.length;
  var offset = array.length;

  for (var index = 0; index < length; index++) {
    array[offset++] = values[index];
  }
  return array;
}

function flatten(values, level) {
  var result = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];

  if (level > 1) {
    values.forEach(function (v) {
      return flatten(v, level - 1, result);
    });
  } else {
    arrayPush(result, values);
  }
  return result;
}

var DEFAULT_COLOR = [0, 0, 0, 255]; // Black

export var PolygonTesselatorExtruded = function () {
  function PolygonTesselatorExtruded(_ref) {
    var polygons = _ref.polygons,
        _ref$getHeight = _ref.getHeight,
        getHeight = _ref$getHeight === undefined ? function (x) {
      return 1000;
    } : _ref$getHeight,
        _ref$getColor = _ref.getColor,
        getColor = _ref$getColor === undefined ? function (x) {
      return DEFAULT_COLOR;
    } : _ref$getColor,
        _ref$wireframe = _ref.wireframe,
        wireframe = _ref$wireframe === undefined ? false : _ref$wireframe,
        _ref$fp = _ref.fp64,
        fp64 = _ref$fp === undefined ? false : _ref$fp;

    _classCallCheck(this, PolygonTesselatorExtruded);

    this.fp64 = fp64;

    // Expensive operation, convert all polygons to arrays
    polygons = polygons.map(function (complexPolygon, polygonIndex) {
      var height = getHeight(polygonIndex) || 0;
      return Polygon.normalize(complexPolygon).map(function (polygon) {
        return polygon.map(function (coord) {
          return [coord[0], coord[1], height];
        });
      });
    });

    var groupedVertices = polygons;
    this.groupedVertices = polygons;
    var pointCount = getPointCount(polygons);
    this.pointCount = pointCount;
    this.wireframe = wireframe;

    this.attributes = {};

    var positionsJS = calculatePositionsJS({ groupedVertices: groupedVertices, pointCount: pointCount, wireframe: wireframe });
    Object.assign(this.attributes, {
      positions: calculatePositions(positionsJS, this.fp64),
      indices: calculateIndices({ groupedVertices: groupedVertices, wireframe: wireframe }),
      normals: calculateNormals({ groupedVertices: groupedVertices, pointCount: pointCount, wireframe: wireframe }),
      // colors: calculateColors({groupedVertices, wireframe, getColor}),
      pickingColors: calculatePickingColors({ groupedVertices: groupedVertices, pointCount: pointCount, wireframe: wireframe })
    });
  }

  _createClass(PolygonTesselatorExtruded, [{
    key: 'indices',
    value: function indices() {
      return this.attributes.indices;
    }
  }, {
    key: 'positions',
    value: function positions() {
      return this.attributes.positions;
    }
  }, {
    key: 'normals',
    value: function normals() {
      return this.attributes.normals;
    }
  }, {
    key: 'colors',
    value: function colors() {
      var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref2$getColor = _ref2.getColor,
          getColor = _ref2$getColor === undefined ? function (x) {
        return DEFAULT_COLOR;
      } : _ref2$getColor;

      var groupedVertices = this.groupedVertices,
          pointCount = this.pointCount,
          wireframe = this.wireframe;

      return calculateColors({ groupedVertices: groupedVertices, pointCount: pointCount, wireframe: wireframe, getColor: getColor });
    }
  }, {
    key: 'pickingColors',
    value: function pickingColors() {
      return this.attributes.pickingColors;
    }

    // updateTriggers: {
    //   positions: ['getHeight'],
    //   colors: ['getColors']
    //   pickingColors: 'none'
    // }

  }]);

  return PolygonTesselatorExtruded;
}();

// Count number of points in a list of complex polygons
function getPointCount(polygons) {
  return polygons.reduce(function (points, polygon) {
    return points + Polygon.getVertexCount(polygon);
  }, 0);
}

function calculateIndices(_ref3) {
  var groupedVertices = _ref3.groupedVertices,
      _ref3$wireframe = _ref3.wireframe,
      wireframe = _ref3$wireframe === undefined ? false : _ref3$wireframe;

  // adjust index offset for multiple polygons
  var multiplier = wireframe ? 2 : 5;
  var offsets = [];
  groupedVertices.reduce(function (vertexIndex, vertices) {
    offsets.push(vertexIndex);
    return vertexIndex + Polygon.getVertexCount(vertices) * multiplier;
  }, 0);

  var indices = groupedVertices.map(function (vertices, polygonIndex) {
    return wireframe ? // 1. get sequentially ordered indices of each polygons wireframe
    // 2. offset them by the number of indices in previous polygons
    calculateContourIndices(vertices, offsets[polygonIndex]) : // 1. get triangulated indices for the internal areas
    // 2. offset them by the number of indices in previous polygons
    calculateSurfaceIndices(vertices, offsets[polygonIndex]);
  });

  return new Uint32Array(flatten(indices, 2));
}

// Calculate a flat position array in JS - can be mapped to 32 or 64 bit typed arrays
// Remarks:
// * each top vertex is on 3 surfaces
// * each bottom vertex is on 2 surfaces
function calculatePositionsJS(_ref4) {
  var groupedVertices = _ref4.groupedVertices,
      pointCount = _ref4.pointCount,
      _ref4$wireframe = _ref4.wireframe,
      wireframe = _ref4$wireframe === undefined ? false : _ref4$wireframe;

  var multiplier = wireframe ? 2 : 5;
  var positions = new Float32Array(pointCount * 3 * multiplier);
  var vertexIndex = 0;

  groupedVertices.forEach(function (vertices) {
    var topVertices = flatten(vertices, 3);

    var baseVertices = topVertices.slice(0);
    var i = topVertices.length - 1;
    while (i > 0) {
      baseVertices[i] = 0;
      i -= 3;
    }
    var len = topVertices.length;

    if (wireframe) {
      fillArray({ target: positions, source: topVertices, start: vertexIndex });
      fillArray({ target: positions, source: baseVertices, start: vertexIndex + len });
    } else {
      fillArray({ target: positions, source: topVertices, start: vertexIndex, count: 3 });
      fillArray({
        target: positions,
        source: baseVertices,
        start: vertexIndex + len * 3,
        count: 2
      });
    }
    vertexIndex += len * multiplier;
  });

  return positions;
}

function calculatePositions(positionsJS, fp64) {
  var positionLow = void 0;
  if (fp64) {
    // We only need x, y component
    var vertexCount = positionsJS.length / 3;
    positionLow = new Float32Array(vertexCount * 2);
    for (var i = 0; i < vertexCount; i++) {
      positionLow[i * 2 + 0] = fp64LowPart(positionsJS[i * 3 + 0]);
      positionLow[i * 2 + 1] = fp64LowPart(positionsJS[i * 3 + 1]);
    }
  }
  return { positions: positionsJS, positions64xyLow: positionLow };
}

function calculateNormals(_ref5) {
  var groupedVertices = _ref5.groupedVertices,
      pointCount = _ref5.pointCount,
      wireframe = _ref5.wireframe;

  var up = [0, 0, 1];
  var multiplier = wireframe ? 2 : 5;

  var normals = new Float32Array(pointCount * 3 * multiplier);
  var vertexIndex = 0;

  if (wireframe) {
    return fillArray({ target: normals, source: up, count: pointCount * multiplier });
  }

  groupedVertices.map(function (vertices, polygonIndex) {
    var vertexCount = Polygon.getVertexCount(vertices);

    fillArray({ target: normals, source: up, start: vertexIndex, count: vertexCount });
    vertexIndex += vertexCount * 3;

    var sideNormalsForward = [];
    var sideNormalsBackward = [];

    vertices.forEach(function (polygon) {
      var sideNormals = calculateSideNormals(polygon);
      var firstNormal = sideNormals.slice(0, 3);

      arrayPush(sideNormalsForward, sideNormals);
      arrayPush(sideNormalsForward, firstNormal);

      arrayPush(sideNormalsBackward, firstNormal);
      arrayPush(sideNormalsBackward, sideNormals);
    });

    fillArray({
      target: normals,
      start: vertexIndex,
      count: 2,
      source: sideNormalsForward.concat(sideNormalsBackward)
    });
    vertexIndex += vertexCount * 3 * 4;
  });

  return normals;
}

function calculateSideNormals(vertices) {
  var normals = [];

  var lastVertice = null;
  var _iteratorNormalCompletion = true;
  var _didIteratorError = false;
  var _iteratorError = undefined;

  try {
    for (var _iterator = vertices[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
      var vertice = _step.value;

      if (lastVertice) {
        // vertex[i-1], vertex[i]
        var n = getNormal(lastVertice, vertice);
        arrayPush(normals, n);
      }
      lastVertice = vertice;
    }
  } catch (err) {
    _didIteratorError = true;
    _iteratorError = err;
  } finally {
    try {
      if (!_iteratorNormalCompletion && _iterator.return) {
        _iterator.return();
      }
    } finally {
      if (_didIteratorError) {
        throw _iteratorError;
      }
    }
  }

  return normals;
}

function calculateColors(_ref6) {
  var groupedVertices = _ref6.groupedVertices,
      pointCount = _ref6.pointCount,
      getColor = _ref6.getColor,
      _ref6$wireframe = _ref6.wireframe,
      wireframe = _ref6$wireframe === undefined ? false : _ref6$wireframe;

  var multiplier = wireframe ? 2 : 5;
  var colors = new Uint8ClampedArray(pointCount * 4 * multiplier);
  var vertexIndex = 0;

  groupedVertices.forEach(function (complexPolygon, polygonIndex) {
    var color = getColor(polygonIndex);
    color[3] = Number.isFinite(color[3]) ? color[3] : 255;

    var numVertices = Polygon.getVertexCount(complexPolygon);

    fillArray({ target: colors, source: color, start: vertexIndex, count: numVertices * multiplier });
    vertexIndex += color.length * numVertices * multiplier;
  });

  return colors;
}

function calculatePickingColors(_ref7) {
  var groupedVertices = _ref7.groupedVertices,
      pointCount = _ref7.pointCount,
      _ref7$wireframe = _ref7.wireframe,
      wireframe = _ref7$wireframe === undefined ? false : _ref7$wireframe;

  var multiplier = wireframe ? 2 : 5;
  var colors = new Uint8ClampedArray(pointCount * 3 * multiplier);
  var vertexIndex = 0;

  groupedVertices.forEach(function (vertices, polygonIndex) {
    var numVertices = Polygon.getVertexCount(vertices);
    var color = getPickingColor(polygonIndex);

    fillArray({ target: colors, source: color, start: vertexIndex, count: numVertices * multiplier });
    vertexIndex += color.length * numVertices * multiplier;
  });
  return colors;
}

function calculateContourIndices(vertices, offset) {
  var stride = Polygon.getVertexCount(vertices);
  var indices = [];

  vertices.forEach(function (polygon) {
    indices.push(offset);
    var numVertices = polygon.length;

    // polygon top
    // use vertex pairs for GL.LINES => [0, 1, 1, 2, 2, ..., n-1, n-1, 0]
    for (var i = 1; i < numVertices - 1; i++) {
      indices.push(i + offset, i + offset);
    }
    indices.push(offset);

    // polygon sides
    for (var _i = 0; _i < numVertices - 1; _i++) {
      indices.push(_i + offset, _i + stride + offset);
    }

    offset += numVertices;
  });

  return indices;
}

function drawSurfaceRectangle(targetArray, offset, stride) {
  targetArray.push(offset + stride, offset + stride * 3, offset + stride * 2 + 1, offset + stride * 2 + 1, offset + stride * 3, offset + stride * 4 + 1);
}

function calculateSurfaceIndices(vertices, offset) {
  var stride = Polygon.getVertexCount(vertices);

  var holes = null;
  var holeCount = vertices.length - 1;

  if (holeCount) {
    holes = [];
    var vertexIndex = 0;
    for (var i = 0; i < holeCount; i++) {
      vertexIndex += vertices[i].length;
      holes[i] = vertexIndex;
    }
  }

  var indices = earcut(flatten(vertices, 3), holes, 3).map(function (index) {
    return index + offset;
  });

  vertices.forEach(function (polygon) {
    var numVertices = polygon.length;

    // polygon sides
    for (var _i2 = 0; _i2 < numVertices - 1; _i2++) {
      drawSurfaceRectangle(indices, offset + _i2, stride);
    }

    offset += numVertices;
  });

  return indices;
}

// helpers

// get normal vector of line segment
function getNormal(p1, p2) {
  return [p1[1] - p2[1], p2[0] - p1[0], 0];
}
//# sourceMappingURL=polygon-tesselator-extruded.js.map