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.

// Handles tesselation of polygons with holes
// - 2D surfaces
// - 2D outlines
// - 3D surfaces (top and sides only)
// - 3D wireframes (not yet)
import * as Polygon from './polygon';
import { experimental } from 'deck.gl';
var fillArray = experimental.fillArray,
    fp64LowPart = experimental.fp64LowPart;

// Maybe deck.gl or luma.gl needs to export this

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

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

// This class is set up to allow querying one attribute at a time
// the way the AttributeManager expects it
export var PolygonTesselator = function () {
  function PolygonTesselator(_ref) {
    var polygons = _ref.polygons,
        IndexType = _ref.IndexType;

    _classCallCheck(this, PolygonTesselator);

    // Normalize all polygons
    polygons = polygons.map(function (polygon) {
      return Polygon.normalize(polygon);
    });

    // Count all polygon vertices
    var pointCount = getPointCount(polygons);

    this.polygons = polygons;
    this.pointCount = pointCount;
    this.IndexType = IndexType;

    // TODO: dynamically decide IndexType in tesselator?
    // Check if the vertex count excedes index type limit
    if (IndexType === Uint16Array && pointCount > 65535) {
      throw new Error("Vertex count exceeds browser's limit");
    }

    this.attributes = {
      pickingColors: calculatePickingColors({ polygons: polygons, pointCount: pointCount })
    };
  }

  _createClass(PolygonTesselator, [{
    key: 'updatePositions',
    value: function updatePositions(_ref2) {
      var fp64 = _ref2.fp64,
          extruded = _ref2.extruded;
      var attributes = this.attributes,
          polygons = this.polygons,
          pointCount = this.pointCount;


      attributes.positions = attributes.positions || new Float32Array(pointCount * 3);
      attributes.nextPositions = attributes.nextPositions || new Float32Array(pointCount * 3);

      if (fp64) {
        // We only need x, y component
        attributes.positions64xyLow = attributes.positions64xyLow || new Float32Array(pointCount * 2);
        attributes.nextPositions64xyLow = attributes.nextPositions64xyLow || new Float32Array(pointCount * 2);
      }

      _updatePositions({ cache: attributes, polygons: polygons, extruded: extruded, fp64: fp64 });
    }
  }, {
    key: 'indices',
    value: function indices() {
      var polygons = this.polygons,
          IndexType = this.IndexType;

      return calculateIndices({ polygons: polygons, IndexType: IndexType });
    }
  }, {
    key: 'positions',
    value: function positions() {
      return this.attributes.positions;
    }
  }, {
    key: 'positions64xyLow',
    value: function positions64xyLow() {
      return this.attributes.positions64xyLow;
    }
  }, {
    key: 'nextPositions',
    value: function nextPositions() {
      return this.attributes.nextPositions;
    }
  }, {
    key: 'nextPositions64xyLow',
    value: function nextPositions64xyLow() {
      return this.attributes.nextPositions64xyLow;
    }
  }, {
    key: 'elevations',
    value: function elevations() {
      var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref3$key = _ref3.key,
          key = _ref3$key === undefined ? 'elevations' : _ref3$key,
          _ref3$getElevation = _ref3.getElevation,
          getElevation = _ref3$getElevation === undefined ? function (x) {
        return 100;
      } : _ref3$getElevation;

      var attributes = this.attributes,
          polygons = this.polygons,
          pointCount = this.pointCount;

      var values = updateElevations({ cache: attributes[key], polygons: polygons, pointCount: pointCount, getElevation: getElevation });
      attributes[key] = values;
      return values;
    }
  }, {
    key: 'colors',
    value: function colors() {
      var _ref4 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref4$key = _ref4.key,
          key = _ref4$key === undefined ? 'colors' : _ref4$key,
          _ref4$getColor = _ref4.getColor,
          getColor = _ref4$getColor === undefined ? function (x) {
        return DEFAULT_COLOR;
      } : _ref4$getColor;

      var attributes = this.attributes,
          polygons = this.polygons,
          pointCount = this.pointCount;

      var values = updateColors({ cache: attributes[key], polygons: polygons, pointCount: pointCount, getColor: getColor });
      attributes[key] = values;
      return values;
    }
  }, {
    key: 'pickingColors',
    value: function pickingColors() {
      return this.attributes.pickingColors;
    }
  }]);

  return PolygonTesselator;
}();

// 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);
}

// COunt number of triangles in a list of complex polygons
function getTriangleCount(polygons) {
  return polygons.reduce(function (triangles, polygon) {
    return triangles + Polygon.getTriangleCount(polygon);
  }, 0);
}

// Returns the offsets of each complex polygon in the combined array of all polygons
function getPolygonOffsets(polygons) {
  var offsets = new Array(polygons.length + 1);
  offsets[0] = 0;
  var offset = 0;
  polygons.forEach(function (polygon, i) {
    offset += Polygon.getVertexCount(polygon);
    offsets[i + 1] = offset;
  });
  return offsets;
}

function calculateIndices(_ref5) {
  var polygons = _ref5.polygons,
      _ref5$IndexType = _ref5.IndexType,
      IndexType = _ref5$IndexType === undefined ? Uint32Array : _ref5$IndexType;

  // Calculate length of index array (3 * number of triangles)
  var indexCount = 3 * getTriangleCount(polygons);
  var offsets = getPolygonOffsets(polygons);

  // Allocate the attribute
  var attribute = new IndexType(indexCount);

  // 1. get triangulated indices for the internal areas
  // 2. offset them by the number of indices in previous polygons
  var i = 0;
  polygons.forEach(function (polygon, polygonIndex) {
    var _iteratorNormalCompletion = true;
    var _didIteratorError = false;
    var _iteratorError = undefined;

    try {
      for (var _iterator = Polygon.getSurfaceIndices(polygon)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
        var index = _step.value;

        attribute[i++] = index + offsets[polygonIndex];
      }
    } catch (err) {
      _didIteratorError = true;
      _iteratorError = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion && _iterator.return) {
          _iterator.return();
        }
      } finally {
        if (_didIteratorError) {
          throw _iteratorError;
        }
      }
    }
  });

  return attribute;
}

function _updatePositions(_ref6) {
  var _ref6$cache = _ref6.cache,
      positions = _ref6$cache.positions,
      positions64xyLow = _ref6$cache.positions64xyLow,
      nextPositions = _ref6$cache.nextPositions,
      nextPositions64xyLow = _ref6$cache.nextPositions64xyLow,
      polygons = _ref6.polygons,
      extruded = _ref6.extruded,
      fp64 = _ref6.fp64;

  // Flatten out all the vertices of all the sub subPolygons
  var i = 0;
  var nextI = 0;
  var startVertex = null;

  var pushStartVertex = function pushStartVertex(x, y, z, xLow, yLow) {
    if (extruded) {
      // Save first vertex for setting nextPositions at the end of the loop
      startVertex = { x: x, y: y, z: z, xLow: xLow, yLow: yLow };
    }
  };

  var popStartVertex = function popStartVertex() {
    if (startVertex) {
      nextPositions[nextI * 3] = startVertex.x;
      nextPositions[nextI * 3 + 1] = startVertex.y;
      nextPositions[nextI * 3 + 2] = startVertex.z;
      if (fp64) {
        nextPositions64xyLow[nextI * 2] = startVertex.xLow;
        nextPositions64xyLow[nextI * 2 + 1] = startVertex.yLow;
      }
      nextI++;
    }
    startVertex = null;
  };

  polygons.forEach(function (polygon, polygonIndex) {
    Polygon.forEachVertex(polygon, function (vertex, vertexIndex) {
      // eslint-disable-line
      var x = vertex[0];
      var y = vertex[1];
      var z = vertex[2] || 0;
      var xLow = void 0;
      var yLow = void 0;

      positions[i * 3] = x;
      positions[i * 3 + 1] = y;
      positions[i * 3 + 2] = z;
      if (fp64) {
        xLow = fp64LowPart(x);
        yLow = fp64LowPart(y);
        positions64xyLow[i * 2] = xLow;
        positions64xyLow[i * 2 + 1] = yLow;
      }
      i++;

      if (extruded && vertexIndex > 0) {
        nextPositions[nextI * 3] = x;
        nextPositions[nextI * 3 + 1] = y;
        nextPositions[nextI * 3 + 2] = z;
        if (fp64) {
          nextPositions64xyLow[nextI * 2] = xLow;
          nextPositions64xyLow[nextI * 2 + 1] = yLow;
        }
        nextI++;
      }
      if (vertexIndex === 0) {
        popStartVertex();
        pushStartVertex(x, y, z, xLow, yLow);
      }
    });
  });
  popStartVertex();
}

function updateElevations(_ref7) {
  var cache = _ref7.cache,
      polygons = _ref7.polygons,
      pointCount = _ref7.pointCount,
      getElevation = _ref7.getElevation;

  var elevations = cache || new Float32Array(pointCount);
  var i = 0;
  polygons.forEach(function (complexPolygon, polygonIndex) {
    // Calculate polygon color
    var height = getElevation(polygonIndex);

    var vertexCount = Polygon.getVertexCount(complexPolygon);
    fillArray({ target: elevations, source: [height], start: i, count: vertexCount });
    i += vertexCount;
  });
  return elevations;
}

function updateColors(_ref8) {
  var cache = _ref8.cache,
      polygons = _ref8.polygons,
      pointCount = _ref8.pointCount,
      getColor = _ref8.getColor;

  var colors = cache || new Uint8ClampedArray(pointCount * 4);
  var i = 0;
  polygons.forEach(function (complexPolygon, polygonIndex) {
    // Calculate polygon color
    var color = getColor(polygonIndex);
    if (isNaN(color[3])) {
      color[3] = 255;
    }

    var vertexCount = Polygon.getVertexCount(complexPolygon);
    fillArray({ target: colors, source: color, start: i, count: vertexCount });
    i += color.length * vertexCount;
  });
  return colors;
}

function calculatePickingColors(_ref9) {
  var polygons = _ref9.polygons,
      pointCount = _ref9.pointCount;

  var attribute = new Uint8ClampedArray(pointCount * 3);
  var i = 0;
  polygons.forEach(function (complexPolygon, polygonIndex) {
    var color = getPickingColor(polygonIndex);
    var vertexCount = Polygon.getVertexCount(complexPolygon);
    fillArray({ target: attribute, source: color, start: i, count: vertexCount });
    i += color.length * vertexCount;
  });
  return attribute;
}
//# sourceMappingURL=polygon-tesselator.js.map