"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = exports.ScreenGrid = void 0;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var THREE = _interopRequireWildcard(require("three"));
var _GlobeLayer = _interopRequireDefault(require("../Core/Prefab/Globe/GlobeLayer"));
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function isIntersectedOrOverlaped(a, b) {
  return !(a.left > b.right || a.right < b.left || a.top > b.bottom || a.bottom < b.top);
}
var frustum = new THREE.Frustum();

// A grid to manage labels on the screen.
var ScreenGrid = /*#__PURE__*/function () {
  function ScreenGrid() {
    var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 12;
    var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10;
    var width = arguments.length > 2 ? arguments[2] : undefined;
    var height = arguments.length > 3 ? arguments[3] : undefined;
    (0, _classCallCheck2["default"])(this, ScreenGrid);
    this.x = x;
    this.y = y;
    this.grid = [];
    this.visible = [];
    this.resize();
    this.reset();
    this.width = width;
    this.height = height;
  }

  // Reset each cell and hidden and visible.
  (0, _createClass2["default"])(ScreenGrid, [{
    key: "reset",
    value: function reset() {
      for (var i = 0; i < this.x; i++) {
        for (var j = 0; j < this.y; j++) {
          // Splice is prefered to creating a new array, in term of memory
          this.grid[i][j].splice(0, this.grid[i][j].length);
        }
      }
      this.visible = [];
    }

    // Add rows if needed — but don't delete anything else. Columns are taken
    // care in reset().
  }, {
    key: "resize",
    value: function resize() {
      for (var i = 0; i < this.x; i++) {
        if (!this.grid[i]) {
          this.grid[i] = [];
        }
        for (var j = 0; j < this.y; j++) {
          if (!this.grid[i][j]) {
            this.grid[i][j] = [];
          }
        }
      }
    }

    // Insert a label using its boundaries. It is either added to hidden or
    // visible, given the result. The grid is populated with true for every
    // filled cell.
  }, {
    key: "insert",
    value: function insert(obj) {
      var minx = Math.max(0, Math.floor(obj.boundaries.left / this.width * this.x));
      var maxx = Math.min(this.x - 1, Math.floor(obj.boundaries.right / this.width * this.x));
      var miny = Math.max(0, Math.floor(obj.boundaries.top / this.height * this.y));
      var maxy = Math.min(this.y - 1, Math.floor(obj.boundaries.bottom / this.height * this.y));
      for (var i = minx; i <= maxx; i++) {
        for (var j = miny; j <= maxy; j++) {
          if (this.grid[i][j].length > 0) {
            if (this.grid[i][j].some(function (l) {
              return isIntersectedOrOverlaped(l.boundaries, obj.boundaries);
            })) {
              obj.visible = false;
              return false;
            }
          }
        }
      }
      for (var _i = minx; _i <= maxx; _i++) {
        for (var _j = miny; _j <= maxy; _j++) {
          this.grid[_i][_j].push(obj);
        }
      }
      return true;
    }
  }]);
  return ScreenGrid;
}();
exports.ScreenGrid = ScreenGrid;
var worldPosition = new THREE.Vector3();

/**
 * This renderer is inspired by the
 * [`THREE.CSS2DRenderer`](https://threejs.org/docs/#examples/en/renderers/CSS2DRenderer).
 * It is instanciated in `c3DEngine`, as another renderer to handles Labels.
 */
var Label2DRenderer = /*#__PURE__*/function () {
  function Label2DRenderer() {
    (0, _classCallCheck2["default"])(this, Label2DRenderer);
    this.domElement = document.createElement('div');
    this.domElement.style.overflow = 'hidden';
    this.domElement.style.position = 'absolute';
    this.domElement.style.top = '0';
    this.domElement.style.height = '100%';
    this.domElement.style.width = '100%';
    this.domElement.style.zIndex = 1;

    // Used to destroy labels that are not added to the DOM
    this.garbage = document.createElement('div');
    this.garbage.style.display = 'none';
    this.domElement.appendChild(this.garbage);
    this.halfWidth = 0;
    this.halfHeight = 0;
    this.grid = new ScreenGrid();
    this.infoTileLayer = undefined;
  }
  (0, _createClass2["default"])(Label2DRenderer, [{
    key: "setSize",
    value: function setSize(width, height) {
      this.domElement.style.width = "".concat(width);
      this.domElement.style.height = "".concat(height);
      this.halfWidth = width / 2;
      this.halfHeight = height / 2;
      this.grid.width = width;
      this.grid.height = height;
      this.grid.x = Math.ceil(width / 20);
      this.grid.y = Math.ceil(height / 20);
      this.grid.resize();
    }
  }, {
    key: "registerLayer",
    value: function registerLayer(layer) {
      this.domElement.appendChild(layer.domElement.dom);
    }
  }, {
    key: "render",
    value: function render(scene, camera) {
      var _this = this;
      var labelLayers = this.infoTileLayer && this.infoTileLayer.layer.attachedLayers.filter(function (l) {
        return l.isLabelLayer && l.visible;
      });
      if (labelLayers.length == 0) {
        return;
      }
      this.grid.reset();

      // set camera frustum
      frustum.setFromProjectionMatrix(camera.projectionMatrix);
      labelLayers.forEach(function (labelLayer) {
        labelLayer.submittedLabelNodes.forEach(function (labelsNode) {
          labelsNode.labels.forEach(function (label) {
            labelsNode.updatePosition(label);
            _this.culling(label, camera);
          });
          labelsNode.domElements.labels.show();
          labelsNode.needsUpdate = false;
        });
      });

      // sort by order, then by visibility inside those subsets
      // https://docs.mapbox.com/help/troubleshooting/optimize-map-label-placement/#label-hierarchy
      this.grid.visible.sort(function (a, b) {
        var r = b.order - a.order;
        if (r == 0) {
          if (!a.visible && b.visible) {
            return 1;
          } else {
            return -1;
          }
        } else {
          return r;
        }
      });
      this.grid.visible.forEach(function (l) {
        if (_this.grid.insert(l)) {
          l.visible = true;
          l.updateCSSPosition();
        } else {
          l.visible = false;
        }
      });
      labelLayers.forEach(function (labelLayer) {
        labelLayer.toHide.children.forEach(function (labelsNode) {
          var _labelsNode$domElemen;
          return (_labelsNode$domElemen = labelsNode.domElements) === null || _labelsNode$domElemen === void 0 ? void 0 : _labelsNode$domElemen.labels.hide();
        });
        labelLayer.toHide.clear();
      });
    }
  }, {
    key: "culling",
    value: function culling(label, camera) {
      label.getWorldPosition(worldPosition);
      // Check if the frustum contains tle label
      if (!frustum.containsPoint(worldPosition.applyMatrix4(camera.matrixWorldInverse)) ||
      // Check if globe horizon culls the label
      // Do some horizon culling (if possible) if the tiles level is small enough.
      label.horizonCullingPoint && _GlobeLayer["default"].horizonCulling(label.horizonCullingPoint) ||
      // Check if content isn't present in visible labels
      this.grid.visible.some(function (l) {
        // TODO for icon without text filter by position
        var textContent = label.content.textContent;
        return textContent !== '' && l.content.textContent.toLowerCase() == textContent.toLowerCase();
      })) {
        label.visible = false;
      } else {
        // projecting world position label
        worldPosition.applyMatrix4(camera.projectionMatrix);
        label.updateProjectedPosition(worldPosition.x * this.halfWidth + this.halfWidth, -worldPosition.y * this.halfHeight + this.halfHeight);
        this.grid.visible.push(label);
      }
    }
  }, {
    key: "removeLabelDOM",
    value: function removeLabelDOM(label) {
      this.garbage.appendChild(label.content);
      this.garbage.innerHTML = '';
    }
  }]);
  return Label2DRenderer;
}();
var _default = Label2DRenderer;
exports["default"] = _default;