"use strict";

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

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

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

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

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

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

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

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

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

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

var _GeometryLayer2 = _interopRequireDefault(require("./GeometryLayer"));

var _OBB = _interopRequireDefault(require("../Renderer/OBB"));

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

var _dTilesProcessing = require("../Process/3dTilesProcessing");

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function () { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

function _createForOfIteratorHelper(o) { if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (o = _unsupportedIterableToArray(o))) { var i = 0; var F = function () {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var it, normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

var _update = (0, _dTilesProcessing.process3dTilesNode)();
/** @classdesc
 * Class mapping 3D Tiles extensions names to their associated parsing methods.
 */


var $3DTilesExtensions = /*#__PURE__*/function () {
  function $3DTilesExtensions() {
    (0, _classCallCheck2["default"])(this, $3DTilesExtensions);
    this.extensionsMap = new Map();
  }
  /**
   * Register a 3D Tiles extension: Maps an extension name to its parser
   * @param {String} extensionName - Name of the extension
   * @param {Function} parser - The function for parsing the extension
   */


  (0, _createClass2["default"])($3DTilesExtensions, [{
    key: "registerExtension",
    value: function registerExtension(extensionName, parser) {
      this.extensionsMap.set(extensionName, parser);
    }
    /**
     * Get the parser of a given extension
     * @param {String} extensionName - Name of the extension
     * @returns {Function} - The function for parsing the extension
     */

  }, {
    key: "getParser",
    value: function getParser(extensionName) {
      return this.extensionsMap.get(extensionName);
    }
    /**
     * Test if an extension is registered
     * @param {String} extensionName - Name of the extension
     * @returns {boolean} - true if the extension is registered and false
     * otherwise
     */

  }, {
    key: "isExtensionRegistered",
    value: function isExtensionRegistered(extensionName) {
      return this.extensionsMap.has(extensionName);
    }
  }]);
  return $3DTilesExtensions;
}();
/**
 * Global object holding 3D Tiles extensions. Extensions must be registered
 * with their parser by the client.
 * @type {$3DTilesExtensions}
 */


var $3dTilesExtensions = new $3DTilesExtensions();
/** @classdesc
 * Abstract class for 3DTiles Extensions. Extensions implemented by the user
 * must inherit from this class. Example of extension extending this class:
 *  [BatchTableHierarchyExtension]{@link BatchTableHierarchyExtension}
 */

exports.$3dTilesExtensions = $3dTilesExtensions;

var $3dTilesAbstractExtension = /*#__PURE__*/function () {
  function $3dTilesAbstractExtension() {
    (0, _classCallCheck2["default"])(this, $3dTilesAbstractExtension);

    if (this.constructor === $3dTilesAbstractExtension) {
      throw new TypeError('Abstract class "AbstractExtension" ' + 'cannot be instantiated directly');
    }
  }
  /**
   * Method to get the displayable information related to a given feature
   * from an extension. All extensions must implement it (event if it
   * returns an empty object).
   * @param {integer} featureId - id of the feature
   */
  // disable warning saying that we don't use 'this' in this method
  // eslint-disable-next-line


  (0, _createClass2["default"])($3dTilesAbstractExtension, [{
    key: "getPickingInfo",
    value: function getPickingInfo() {
      throw new Error('You must implement getPickingInfo function ' + 'in your extension');
    }
  }]);
  return $3dTilesAbstractExtension;
}();

exports.$3dTilesAbstractExtension = $3dTilesAbstractExtension;
var extent = new _Extent["default"]('EPSG:4326', 0, 0, 0, 0);

function getBox(volume, inverseTileTransform) {
  if (volume.region) {
    var region = volume.region;
    extent.set(THREE.MathUtils.radToDeg(region[0]), THREE.MathUtils.radToDeg(region[2]), THREE.MathUtils.radToDeg(region[1]), THREE.MathUtils.radToDeg(region[3]));
    var box = new _OBB["default"]().setFromExtent(extent);
    box.updateZ(region[4], region[5]); // at this point box.matrix = box.epsg4978_from_local, so
    // we transform it in parent_from_local by using parent's epsg4978_from_local
    // which from our point of view is epsg4978_from_parent.
    // box.matrix = (epsg4978_from_parent ^ -1) * epsg4978_from_local
    //            =  parent_from_epsg4978 * epsg4978_from_local
    //            =  parent_from_local

    box.matrix.premultiply(inverseTileTransform); // update position, rotation and scale

    box.matrix.decompose(box.position, box.quaternion, box.scale);
    return {
      region: box
    };
  } else if (volume.box) {
    // TODO: only works for axis aligned boxes
    var _box = volume.box; // box[0], box[1], box[2] = center of the box
    // box[3], box[4], box[5] = x axis direction and half-length
    // box[6], box[7], box[8] = y axis direction and half-length
    // box[9], box[10], box[11] = z axis direction and half-length

    var center = new THREE.Vector3(_box[0], _box[1], _box[2]);
    var w = center.x - _box[3];
    var e = center.x + _box[3];
    var s = center.y - _box[7];
    var n = center.y + _box[7];
    var b = center.z - _box[11];
    var t = center.z + _box[11];
    return {
      box: new THREE.Box3(new THREE.Vector3(w, s, b), new THREE.Vector3(e, n, t))
    };
  } else if (volume.sphere) {
    var sphere = new THREE.Sphere(new THREE.Vector3(volume.sphere[0], volume.sphere[1], volume.sphere[2]), volume.sphere[3]);
    return {
      sphere: sphere
    };
  }
}

function $3dTilesIndex(tileset, baseURL) {
  var counter = 1;
  this.index = {};
  var inverseTileTransform = new THREE.Matrix4();

  var recurse = function (node, baseURL, parent) {
    // compute transform (will become Object3D.matrix when the object is downloaded)
    node.transform = node.transform ? new THREE.Matrix4().fromArray(node.transform) : undefined; // The only reason to store _worldFromLocalTransform is because of extendTileset where we need the
    // transform chain for one node.

    node._worldFromLocalTransform = node.transform;

    if (parent && parent._worldFromLocalTransform) {
      if (node.transform) {
        node._worldFromLocalTransform = new THREE.Matrix4().multiplyMatrices(parent._worldFromLocalTransform, node.transform);
      } else {
        node._worldFromLocalTransform = parent._worldFromLocalTransform;
      }
    } // getBox only use inverseTileTransform for volume.region so let's not
    // compute the inverse matrix each time
    // Assumes that node.boundingVolume is defined if node.viewerRequestVolume is undefined


    if (node.viewerRequestVolume && node.viewerRequestVolume.region || node.boundingVolume.region) {
      if (node._worldFromLocalTransform) {
        inverseTileTransform.getInverse(node._worldFromLocalTransform);
      } else {
        inverseTileTransform.identity();
      }
    }

    node.viewerRequestVolume = node.viewerRequestVolume ? getBox(node.viewerRequestVolume, inverseTileTransform) : undefined;
    node.boundingVolume = getBox(node.boundingVolume, inverseTileTransform);
    this.index[counter] = node;
    node.tileId = counter;
    node.baseURL = baseURL;
    counter++;

    if (node.children) {
      var _iterator = _createForOfIteratorHelper(node.children),
          _step;

      try {
        for (_iterator.s(); !(_step = _iterator.n()).done;) {
          var child = _step.value;
          recurse(child, baseURL, node);
        }
      } catch (err) {
        _iterator.e(err);
      } finally {
        _iterator.f();
      }
    }
  }.bind(this);

  recurse(tileset.root, baseURL);

  this.extendTileset = function (tileset, nodeId, baseURL) {
    recurse(tileset.root, baseURL, this.index[nodeId]);
    this.index[nodeId].children = [tileset.root];
    this.index[nodeId].isTileset = true;
  };
}

var C3DTilesLayer = /*#__PURE__*/function (_GeometryLayer) {
  (0, _inherits2["default"])(C3DTilesLayer, _GeometryLayer);

  var _super = _createSuper(C3DTilesLayer);

  /**
   * Constructs a new instance of 3d tiles layer.
   * @constructor
   * @extends GeometryLayer
   *
   * @example
   * // Create a new Layer 3d-tiles For DiscreteLOD
   * const l3dt = new C3DTilesLayer('3dtiles', {
   *      name: '3dtl',
   *      source: new C3DTilesSource({
   *           url: 'https://tileset.json'
   *      })
   * }, view);
   * View.prototype.addLayer.call(view, l3dt);
   *
   * @param      {string}  id - The id of the layer, that should be unique. It is
   * not mandatory, but an error will be emitted if this layer is added a
   * {@link View} that already has a layer going by that id.
   * @param      {object}  config   configuration, all elements in it
   * will be merged as is in the layer.
   * @param {C3TilesSource} config.source The source of 3d Tiles.
   *
   * name.
   * @param  {View}  view  The view
   */
  function C3DTilesLayer(id, config, view) {
    var _this;

    (0, _classCallCheck2["default"])(this, C3DTilesLayer);
    _this = _super.call(this, id, new THREE.Group(), {
      source: config.source
    });
    _this.isC3DTilesLayer = true;
    _this.sseThreshold = config.sseThreshold || 16;
    _this.cleanupDelay = config.cleanupDelay || 1000;

    _this.onTileContentLoaded = config.onTileContentLoaded || function () {};

    _this.protocol = '3d-tiles'; // custom cesium shaders are not functional;

    _this.overrideMaterials = config.overrideMaterials !== undefined ? config.overrideMaterials : true;
    _this.name = config.name;
    _this._cleanableTiles = [];

    var resolve = _this.addInitializationStep();

    _this.source.whenReady.then(function (tileset) {
      _this.tileset = tileset;
      _this.tileIndex = new $3dTilesIndex(tileset, _this.source.baseUrl);
      _this.asset = tileset.asset; // TODO: Move all init3dTilesLayer code to constructor

      (0, _dTilesProcessing.init3dTilesLayer)(view, view.mainLoop.scheduler, (0, _assertThisInitialized2["default"])(_this), tileset.root).then(resolve);
    });

    return _this;
  }

  (0, _createClass2["default"])(C3DTilesLayer, [{
    key: "preUpdate",
    value: function preUpdate() {
      return _dTilesProcessing.pre3dTilesUpdate.bind(this)();
    }
  }, {
    key: "update",
    value: function update(context, layer, node) {
      return _update(context, layer, node);
    }
  }, {
    key: "getObjectToUpdateForAttachedLayers",
    value: function getObjectToUpdateForAttachedLayers(meta) {
      if (meta.content) {
        var result = [];
        meta.content.traverse(function (obj) {
          if (obj.isObject3D && obj.material && obj.layer == meta.layer) {
            result.push(obj);
          }
        });
        var p = meta.parent;

        if (p && p.content) {
          return {
            elements: result,
            parent: p.content
          };
        } else {
          return {
            elements: result
          };
        }
      }
    }
  }]);
  return C3DTilesLayer;
}(_GeometryLayer2["default"]);

var _default = C3DTilesLayer;
exports["default"] = _default;