"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = void 0;

var THREE = _interopRequireWildcard(require("three"));

var _LayerUpdateState = _interopRequireDefault(require("../Layer/LayerUpdateState"));

var _ObjectRemovalHelper = _interopRequireDefault(require("./ObjectRemovalHelper"));

var _handlerNodeError = _interopRequireDefault(require("./handlerNodeError"));

var _Coordinates = _interopRequireDefault(require("../Core/Geographic/Coordinates"));

var _Extent = _interopRequireDefault(require("../Core/Geographic/Extent"));

var _extent = new _Extent["default"]('EPSG:4326', 0, 0, 0, 0);

var coord = new _Coordinates["default"]('EPSG:4326', 0, 0, 0);
var vector = new THREE.Vector3();
var tmp = new THREE.Vector3();

function applyOffset(obj, offset, quaternion) {
  var offsetAltitude = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;

  if (obj.geometry) {
    if (obj.geometry.isBufferGeometry) {
      var count = obj.geometry.attributes.position.count * 3;

      for (var i = 0; i < count; i += 3) {
        vector.fromArray(obj.geometry.attributes.position.array, i);
        vector.add(offset).applyQuaternion(quaternion);
        vector.z -= offsetAltitude;
        vector.toArray(obj.geometry.attributes.position.array, i);
      }

      obj.geometry.attributes.position.needsUpdate = true;
    } else {
      var _iteratorNormalCompletion = true;
      var _didIteratorError = false;
      var _iteratorError = undefined;

      try {
        for (var _iterator = obj.geometry.vertices[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
          var v = _step.value;
          v.add(offset).applyQuaternion(quaternion);
          v.z -= offsetAltitude;
        }
      } catch (err) {
        _didIteratorError = true;
        _iteratorError = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion && _iterator["return"] != null) {
            _iterator["return"]();
          }
        } finally {
          if (_didIteratorError) {
            throw _iteratorError;
          }
        }
      }

      obj.geometry.verticesNeedUpdate = true;
    }
  }

  obj.children.forEach(function (c) {
    return applyOffset(c, offset, quaternion, offsetAltitude);
  });
}

function assignLayer(object, layer) {
  if (object) {
    object.layer = layer;

    if (object.material) {
      object.material.transparent = layer.opacity < 1.0;
      object.material.opacity = layer.opacity;
      object.material.wireframe = layer.wireframe;

      if (layer.size) {
        object.material.size = layer.size;
      }

      if (layer.linewidth) {
        object.material.linewidth = layer.linewidth;
      }
    }

    object.layers.set(layer.threejsLayer);
    var _iteratorNormalCompletion2 = true;
    var _didIteratorError2 = false;
    var _iteratorError2 = undefined;

    try {
      for (var _iterator2 = object.children[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
        var c = _step2.value;
        assignLayer(c, layer);
      }
    } catch (err) {
      _didIteratorError2 = true;
      _iteratorError2 = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion2 && _iterator2["return"] != null) {
          _iterator2["return"]();
        }
      } finally {
        if (_didIteratorError2) {
          throw _iteratorError2;
        }
      }
    }

    return object;
  }
}

function extentInsideSource(extent, source) {
  return !source.extentInsideLimit(extent) || source.parsedData && !source.parsedData.extent.isPointInside(extent.center(coord));
}

var quaternion = new THREE.Quaternion();
var _default = {
  update: function update(context, layer, node) {
    if (!node.parent && node.children.length) {
      // if node has been removed dispose three.js resource
      _ObjectRemovalHelper["default"].removeChildrenAndCleanupRecursively(layer, node);

      return;
    }

    if (!node.visible) {
      return;
    }

    if (node.layerUpdateState[layer.id] === undefined) {
      node.layerUpdateState[layer.id] = new _LayerUpdateState["default"]();
    }

    if (!node.layerUpdateState[layer.id].canTryUpdate()) {
      return;
    }

    var features = node.children.filter(function (n) {
      return n.layer == layer;
    });

    if (features.length > 0) {
      return features;
    }

    var extentsDestination = node.getExtentsByProjection(layer.source.projection) || [node.extent];
    extentsDestination.forEach(function (e) {
      e.zoom = node.level;
    });
    var extentsSource = [];
    var _iteratorNormalCompletion3 = true;
    var _didIteratorError3 = false;
    var _iteratorError3 = undefined;

    try {
      for (var _iterator3 = extentsDestination[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
        var extentDest = _step3.value;
        var ext = layer.source.projection == extentDest.crs ? extentDest : extentDest.as(layer.source.projection);
        ext.zoom = extentDest.zoom;

        if (extentInsideSource(ext, layer.source)) {
          node.layerUpdateState[layer.id].noMoreUpdatePossible();
          return;
        }

        extentsSource.push(extentDest);
      }
    } catch (err) {
      _didIteratorError3 = true;
      _iteratorError3 = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion3 && _iterator3["return"] != null) {
          _iterator3["return"]();
        }
      } finally {
        if (_didIteratorError3) {
          throw _iteratorError3;
        }
      }
    }

    node.layerUpdateState[layer.id].newTry();
    var command = {
      layer: layer,
      extentsSource: extentsSource,
      view: context.view,
      threejsLayer: layer.threejsLayer,
      requester: node
    };
    return context.scheduler.execute(command).then(function (result) {
      // if request return empty json, WFSProvider.getFeatures return undefined
      result = result[0];

      if (result) {
        // special case for FileSource, as it is not tiled and we need
        // to attach it to the correct node
        if (layer.source && layer.source.isFileSource) {
          for (var _i = 0, _extentsSource = extentsSource; _i < _extentsSource.length; _i++) {
            var extentSrc = _extentsSource[_i];
            var ext = extentSrc.crs == layer.source.projection ? extentSrc : extentSrc.as(layer.source.projection, _extent);
            ext.zoom = extentSrc.zoom;

            if (extentInsideSource(ext, layer.source)) {
              node.layerUpdateState[layer.id].noMoreUpdatePossible();
              return;
            }
          }
        }

        var isApplied = !result.layer;
        result.minAltitude = isNaN(result.minAltitude) ? 0 : result.minAltitude;
        assignLayer(result, layer); // call onMeshCreated callback if needed

        if (layer.onMeshCreated) {
          layer.onMeshCreated(result);
        }

        node.layerUpdateState[layer.id].success();

        if (!node.parent) {
          _ObjectRemovalHelper["default"].removeChildrenAndCleanupRecursively(layer, result);

          return;
        } // We don't use node.matrixWorld here, because feature coordinates are
        // expressed in crs coordinates (which may be different than world coordinates,
        // if node's layer is attached to an Object with a non-identity transformation)


        if (isApplied) {
          // NOTE: now data source provider use cache on Mesh
          // TODO move transform in feature2Mesh
          node.extent.center(coord).as(context.view.referenceCrs, coord).toVector3(tmp).negate();
          quaternion.setFromRotationMatrix(node.matrixWorld).inverse(); // const quaternion = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 0, 1), node.extent.center().geodesicNormal).inverse();

          applyOffset(result, tmp, quaternion, result.minAltitude);
        }

        if (result.minAltitude) {
          result.position.z = result.minAltitude;
        }

        result.layer = layer;
        node.add(result);
        node.updateMatrixWorld();
      } else {
        node.layerUpdateState[layer.id].failure(1, true);
      }
    }, function (err) {
      return (0, _handlerNodeError["default"])(err, node, layer, node.level, context.view);
    });
  }
};
exports["default"] = _default;