define([
  "dojo/_base/declare",
  "style/NodeStyler",
  "CesiumAdaptor/CesiumViewer",
  "helpers/CameraChangeEvent",
], function (declare, NodeStyler, CesiumViewer, CameraChangeEvent) {

  let NodeCache = declare("dojo.client.NodeCache", [], {

    _nodes: [],

    /* in seconds */
    _expiryThreshold: 60,

    add: async function (instances, node) {

      let prim = new Cesium.Primitive({
        geometryInstances: instances,
        appearance: new Cesium.PerInstanceColorAppearance({
          closed: true,
          translucent: false
        })
      });

      let viewer = CesiumViewer.getViewer();
      prim = viewer.scene.primitives.add(prim);

      let primitive = await Cesium.when(prim.readyPromise);

      primitive.id = node.id;
      primitive.level = node.level;

      this._nodes.push({
        id: primitive.id,
        primitive: primitive,
        time: Date.now(),
        isVisible: true,
        hasExpired: false
      });

    },

    updateStyle: function () {

      this._nodes.forEach(n => {
        let ids = n.primitive._instanceIds;
        ids.forEach(id => {
          let attributes = n.primitive.getGeometryInstanceAttributes(id);
          attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(
            NodeStyler.getColor(n.primitive.level)
          );
        });
      });
    },

    hide: function (ids) {
      let currentTime = Date.now();
      let viewer = CesiumViewer.getViewer();

      this._nodes.forEach(n => {
        if (this.hasExpired(currentTime, n.id)) {
          n.hasExpired = true;
          viewer.scene.primitives.remove(n.primitive);
        }
        if (ids.indexOf(n.id) == -1 && !n.hasExpired) {
          n.primitive.show = false;
        }
      });
      newNodes = this._nodes.filter(n => !n.hasExpired);
      this._nodes = newNodes;
    },

    show: function (ids) {

      let currentTime = Date.now();
      this._nodes.forEach(n => {
        let index = ids.findIndex(id => id == n.id);
        if (index != -1) {
          n.primitive.show = true;
          n.isVisible = true;
          n.time = currentTime;
        }
      });
    },

    matchAll: function (ids) {
      this.show(ids);
      this.hide(ids);
    },

    clearAll: function () {
      let viewer = CesiumViewer.getViewer();
      viewer.scene.primitives.removeAll();
      this._nodes = [];
    },

    hasExpired: function (currentTime, id) {
      let index = this._nodes.findIndex(n => n.id == id);
      let creationTime = this._nodes[index].time;
      let diffInSeconds = (currentTime - creationTime) / 1000;
      return (diffInSeconds > this._expiryThreshold);
    },

    contains: function (id) {
      let index = this._nodes.findIndex(n => n.id == id);
      if (index != -1) {
        return true;
      }
      return false;
    }

  });
  let _instance = null;
  if (!_instance) {
    _instance = new NodeCache();
  }
  return _instance;
});