"use strict";

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

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

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

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

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

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

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

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

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

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

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

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

var _OrientedImageMaterial = _interopRequireDefault(require("../Renderer/OrientedImageMaterial"));

var _GeoJsonParser = _interopRequireDefault(require("../Parser/GeoJsonParser"));

var _CameraCalibrationParser = _interopRequireDefault(require("../Parser/CameraCalibrationParser"));

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

var _OrientationUtils = _interopRequireDefault(require("../Utils/OrientationUtils"));

var coord = new _Coordinates["default"]('EPSG:4978', 0, 0, 0);

var commandCancellation = function (cmd) {
  return cmd.requester.id !== cmd.layer.currentPano.id;
};

function updatePano(context, camera, layer) {
  var newPano = layer.mostNearPano(camera.position); // detection of oriented image change

  var currentId = layer.currentPano ? layer.currentPano.id : undefined;

  if (newPano && currentId != newPano.id) {
    layer.currentPano = newPano; // callback to indicate current pano has changed

    layer.onPanoChanged({
      previousPanoPosition: layer.getPreviousPano() ? layer.getPreviousPano().position : undefined,
      currentPanoPosition: layer.getCurrentPano().position,
      nextPanoPosition: layer.getNextPano().position
    }); // prepare informations about the needed textures

    var panoCameras = newPano.geometry[0].properties.idSensors;
    var imagesInfo = layer.cameras.map(function (cam) {
      return {
        cameraId: cam.name,
        panoId: newPano.id,
        as: function as() {},
        toString: function toString() {
          var separator = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
          return "".concat(cam.name).concat(separator).concat(newPano.id);
        }
      };
    }).filter(function (info) {
      return !panoCameras || panoCameras.includes(info.cameraId);
    });
    var command = {
      layer: layer,
      // put informations about image URL as extent to be used by generic DataSourceProvider, OrientedImageSource will use that.
      extentsSource: imagesInfo,
      view: context.view,
      threejsLayer: layer.threejsLayer,
      requester: newPano,
      earlyDropFunction: commandCancellation
    }; // async call to scheduler to get textures

    context.scheduler.execute(command).then(function (textures) {
      if (newPano.id === layer.currentPano.id) {
        layer.material.setTextures(textures, newPano, layer.getCamerasNameFromFeature(newPano));
        layer.material.updateUniforms(context.camera.camera3D);
        context.view.notifyChange(layer, true);
      }
    }, function () {});
  }
}

function updateBackground(layer) {
  if (layer.background && layer.currentPano) {
    layer.background.position.copy(layer.currentPano.position);
    layer.background.updateMatrixWorld();
    layer.background.material = layer.material || layer.background.material;
  }
}

function createBackground(radius) {
  if (!radius || radius <= 0) {
    return undefined;
  }

  var geometry = new THREE.SphereGeometry(radius, 32, 32);
  var material = new THREE.MeshPhongMaterial({
    color: 0x7777ff,
    side: THREE.DoubleSide,
    transparent: true,
    opacity: 0.5,
    wireframe: true
  });
  var sphere = new THREE.Mesh(geometry, material);
  sphere.visible = true;
  sphere.name = 'OrientedImageBackground';
  return sphere;
}
/**
 * @classdesc OrientedImageLayer loads oriented images, and project these textures on the scene.
 * It is design to create an immersive view. </br>
 * It loads a set of panoramic position and orientation,
 * a set of camera calibration file (it's the same set of camera for each panoramic),
 * and a set of texture (each image for each camera for each panoramic), all organised in an {@link OrientedImageSource}. </br>
 * It creates an {@link OrientedImageMaterial} used to do projective texture mapping on the scene.
 * @extends GeometryLayer
 */


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

  /**
   * @constructor
   * @param { string } id - The id of the layer, a unique name.
   * @param { Object } config - configuration of the layer
   * @param { number } config.backgroundDistance - Radius in meter of the sphere used as a background
   * @param { function } config.onPanoChanged - callback fired when current panoramic changes
   * @param { string } config.projection - projection of the view
   * @param { string } config.orientation - Json object, using GeoJSon format to represent points,
   * it's a set of panoramic position and orientation.
   * @param { string } config.calibrations - Json object, representing a set of camera. see [CameraCalibrationParser]{@link module:CameraCalibrationParser}
   * @param { OrientedImageSource } config.source - Source used to build url of texture for each oriented image,
   * a tecture is need for each camera, for each panoramic.
   */
  function OrientedImageLayer(id) {
    var _this;

    var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    (0, _classCallCheck2["default"])(this, OrientedImageLayer);
    _this = (0, _possibleConstructorReturn2["default"])(this, (0, _getPrototypeOf2["default"])(OrientedImageLayer).call(this, id, new THREE.Group(), config));
    _this.background = config.background || createBackground(config.backgroundDistance);
    _this.isOrientedImageLayer = true;

    if (_this.background) {
      _this.object3d.add(_this.background);
    } // currentPano is the current point, means it's the closest from the camera


    _this.currentPano = undefined; // store a callback to fire event when current panoramic change

    _this.onPanoChanged = config.onPanoChanged || function () {}; // function to get cameras name from panoramic feature


    _this.getCamerasNameFromFeature = config.getCamerasNameFromFeature || function () {}; // panos is an array of feature point, representing many panoramics.
    // for each point, there is a position and a quaternion attribute.


    var p1 = _GeoJsonParser["default"].parse(config.orientation, {
      mergeFeatures: false,
      crsOut: config.projection
    }).then(function (res) {
      _this.panos = res.features; // we need to know if we are in globe view, for orientation.

      var isGlobe = config.projection == 'EPSG:4978'; // add position and quaternion attributes from point feature

      var i = 0;
      var _iteratorNormalCompletion = true;
      var _didIteratorError = false;
      var _iteratorError = undefined;

      try {
        for (var _iterator = _this.panos[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
          var pano = _step.value;
          // set position
          coord.crs = pano.crs;
          coord.setFromArray(pano.vertices);
          pano.position = coord.toVector3(); // set quaternion

          pano.quaternion = _OrientationUtils["default"].quaternionFromAttitude(pano.geometry[0].properties, coord, isGlobe); // TODO clean DataSourceProvider, so that we don't have this hack to do

          pano.material = {};
          pano.id = pano.geometry[0].properties.id;
          pano.index = i++;
        }
      } catch (err) {
        _didIteratorError = true;
        _iteratorError = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion && _iterator["return"] != null) {
            _iterator["return"]();
          }
        } finally {
          if (_didIteratorError) {
            throw _iteratorError;
          }
        }
      }
    }); // array of cameras, represent the projective texture configuration for each panoramic.


    var p2 = _CameraCalibrationParser["default"].parse(config.calibration, config).then(function (c) {
      _this.cameras = c; // create the material

      _this.material = new _OrientedImageMaterial["default"](_this.cameras, config);
    }); // wait for the twos promises to tell layer is ready.


    _this.whenReady = Promise.all([p1, p2]).then(function () {
      _this.ready = true;
      return (0, _assertThisInitialized2["default"])(_this);
    });
    return _this;
  } // eslint-disable-next-line


  (0, _createClass2["default"])(OrientedImageLayer, [{
    key: "update",
    value: function update() {}
  }, {
    key: "preUpdate",
    value: function preUpdate(context) {
      updatePano(context, context.camera.camera3D, this);
      this.material.updateUniforms(context.camera.camera3D);
      updateBackground(this);
    }
  }, {
    key: "getNextPano",
    value: function getNextPano() {
      var index = (this.currentPano.index + 1) % this.panos.length;
      return this.panos[index];
    }
  }, {
    key: "getCurrentPano",
    value: function getCurrentPano() {
      return this.currentPano;
    }
  }, {
    key: "getPreviousPano",
    value: function getPreviousPano() {
      var index = (this.currentPano.index - 1) % this.panos.length;
      return this.panos[index];
    }
    /**
     * Delete background, but doesn't delete OrientedImageLayer.material. For the moment, this material visibility is set to false.
     * You need to replace OrientedImageLayer.material applied on each object, if you want to continue displaying them.
     * This issue (see #1018 {@link https://github.com/iTowns/itowns/issues/1018}) will be fixed when OrientedImageLayer will be a ColorLayer.
     */

  }, {
    key: "delete",
    value: function _delete() {
      (0, _get2["default"])((0, _getPrototypeOf2["default"])(OrientedImageLayer.prototype), "delete", this).call(this);
      this.material.visible = false;
      console.warn('You need to replace OrientedImageLayer.material applied on each object. This issue will be fixed when OrientedImageLayer will be a ColorLayer. the material visibility is set to false. To follow issue see https://github.com/iTowns/itowns/issues/1018');
    }
  }, {
    key: "mostNearPano",
    value: function mostNearPano(position) {
      var minDistance = Infinity;
      var nearPano;
      var _iteratorNormalCompletion2 = true;
      var _didIteratorError2 = false;
      var _iteratorError2 = undefined;

      try {
        for (var _iterator2 = this.panos[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
          var pano = _step2.value;
          var distance = position.distanceTo(pano.position);

          if (distance < minDistance) {
            minDistance = distance;
            nearPano = pano;
          }
        }
      } catch (err) {
        _didIteratorError2 = true;
        _iteratorError2 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion2 && _iterator2["return"] != null) {
            _iterator2["return"]();
          }
        } finally {
          if (_didIteratorError2) {
            throw _iteratorError2;
          }
        }
      }

      return nearPano;
    }
  }, {
    key: "boostLight",
    set: function set(value) {
      this.material.uniforms.boostLight.value = value;
    },
    get: function get() {
      return this.material.uniforms.boostLight.value;
    }
  }]);
  return OrientedImageLayer;
}(_GeometryLayer2["default"]);

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