import { EVENTS } from './constants';
import { deepEqual } from '../utils/deep-equal';
const PLACEMENTS = {
  'top-left': {
    top: 0,
    left: 0
  },
  'top-right': {
    top: 0,
    right: 0
  },
  'bottom-left': {
    bottom: 0,
    left: 0
  },
  'bottom-right': {
    bottom: 0,
    right: 0
  },
  fill: {
    top: 0,
    left: 0,
    bottom: 0,
    right: 0
  }
};
const DEFAULT_PLACEMENT = 'top-left';
const ROOT_CONTAINER_ID = '__root';
export class WidgetManager {
  constructor({
    deck,
    parentElement
  }) {
    this.deck = void 0;
    this.parentElement = void 0;
    this.defaultWidgets = [];
    this.widgets = [];
    this.resolvedWidgets = [];
    this.containers = {};
    this.lastViewports = {};
    this.deck = deck;
    this.parentElement = parentElement;
  }

  getWidgets() {
    return this.resolvedWidgets;
  }

  setProps(props) {
    if (props.widgets && !deepEqual(props.widgets, this.widgets, 1)) {
      this._setWidgets(props.widgets);
    }
  }

  finalize() {
    for (const widget of this.getWidgets()) {
      this._remove(widget);
    }

    this.defaultWidgets.length = 0;
    this.resolvedWidgets.length = 0;

    for (const id in this.containers) {
      this.containers[id].remove();
    }
  }

  addDefault(widget) {
    if (!this.defaultWidgets.find(w => w.id === widget.id)) {
      this._add(widget);

      this.defaultWidgets.push(widget);

      this._setWidgets(this.widgets);
    }
  }

  _setWidgets(nextWidgets) {
    const oldWidgetMap = {};

    for (const widget of this.resolvedWidgets) {
      oldWidgetMap[widget.id] = widget;
    }

    this.resolvedWidgets.length = 0;

    for (const widget of this.defaultWidgets) {
      oldWidgetMap[widget.id] = null;
      this.resolvedWidgets.push(widget);
    }

    for (let widget of nextWidgets) {
      const oldWidget = oldWidgetMap[widget.id];

      if (!oldWidget) {
        this._add(widget);
      } else if (oldWidget.viewId !== widget.viewId || oldWidget.placement !== widget.placement) {
        this._remove(oldWidget);

        this._add(widget);
      } else if (widget !== oldWidget) {
        oldWidget.setProps(widget.props);
        widget = oldWidget;
      }

      oldWidgetMap[widget.id] = null;
      this.resolvedWidgets.push(widget);
    }

    for (const id in oldWidgetMap) {
      const oldWidget = oldWidgetMap[id];

      if (oldWidget) {
        this._remove(oldWidget);
      }
    }

    this.widgets = nextWidgets;
  }

  _add(widget) {
    const {
      viewId = null,
      placement = DEFAULT_PLACEMENT
    } = widget;
    const element = widget.onAdd({
      deck: this.deck,
      viewId
    });

    if (element) {
      this._getContainer(viewId, placement).append(element);
    }

    widget._element = element;
  }

  _remove(widget) {
    widget.onRemove();

    if (widget._element) {
      widget._element.remove();
    }

    widget._element = undefined;
  }

  _getContainer(viewId, placement) {
    const containerId = viewId || ROOT_CONTAINER_ID;
    let viewContainer = this.containers[containerId];

    if (!viewContainer) {
      var _this$parentElement;

      viewContainer = document.createElement('div');
      viewContainer.style.pointerEvents = 'none';
      viewContainer.style.position = 'absolute';
      viewContainer.style.overflow = 'hidden';
      (_this$parentElement = this.parentElement) === null || _this$parentElement === void 0 ? void 0 : _this$parentElement.append(viewContainer);
      this.containers[containerId] = viewContainer;
    }

    let container = viewContainer.querySelector(`.${placement}`);

    if (!container) {
      container = document.createElement('div');
      container.className = placement;
      container.style.position = 'absolute';
      Object.assign(container.style, PLACEMENTS[placement]);
      viewContainer.append(container);
    }

    return container;
  }

  _updateContainers() {
    const canvasWidth = this.deck.width;
    const canvasHeight = this.deck.height;

    for (const id in this.containers) {
      const viewport = this.lastViewports[id] || null;
      const visible = id === ROOT_CONTAINER_ID || viewport;
      const container = this.containers[id];

      if (visible) {
        container.style.display = 'block';
        container.style.left = `${viewport ? viewport.x : 0}px`;
        container.style.top = `${viewport ? viewport.y : 0}px`;
        container.style.width = `${viewport ? viewport.width : canvasWidth}px`;
        container.style.height = `${viewport ? viewport.height : canvasHeight}px`;
      } else {
        container.style.display = 'none';
      }
    }
  }

  onRedraw({
    viewports,
    layers
  }) {
    const viewportsById = viewports.reduce((acc, v) => {
      acc[v.id] = v;
      return acc;
    }, {});
    const {
      lastViewports
    } = this;

    for (const widget of this.getWidgets()) {
      const {
        viewId
      } = widget;

      if (viewId) {
        const viewport = viewportsById[viewId];

        if (viewport) {
          var _widget$onRedraw;

          if (widget.onViewportChange && !viewport.equals(lastViewports[viewId])) {
            widget.onViewportChange(viewport);
          }

          (_widget$onRedraw = widget.onRedraw) === null || _widget$onRedraw === void 0 ? void 0 : _widget$onRedraw.call(widget, {
            viewports: [viewport],
            layers
          });
        }
      } else {
        var _widget$onRedraw2;

        if (widget.onViewportChange) {
          for (const viewport of viewports) {
            if (!viewport.equals(lastViewports[viewport.id])) {
              widget.onViewportChange(viewport);
            }
          }
        }

        (_widget$onRedraw2 = widget.onRedraw) === null || _widget$onRedraw2 === void 0 ? void 0 : _widget$onRedraw2.call(widget, {
          viewports,
          layers
        });
      }
    }

    this.lastViewports = viewportsById;

    this._updateContainers();
  }

  onHover(info, event) {
    for (const widget of this.getWidgets()) {
      var _info$viewport;

      const {
        viewId
      } = widget;

      if (!viewId || viewId === ((_info$viewport = info.viewport) === null || _info$viewport === void 0 ? void 0 : _info$viewport.id)) {
        var _widget$onHover;

        (_widget$onHover = widget.onHover) === null || _widget$onHover === void 0 ? void 0 : _widget$onHover.call(widget, info, event);
      }
    }
  }

  onEvent(info, event) {
    const eventOptions = EVENTS[event.type];

    if (!eventOptions) {
      return;
    }

    for (const widget of this.getWidgets()) {
      var _info$viewport2;

      const {
        viewId
      } = widget;

      if (!viewId || viewId === ((_info$viewport2 = info.viewport) === null || _info$viewport2 === void 0 ? void 0 : _info$viewport2.id)) {
        var _widget$eventOptions$;

        (_widget$eventOptions$ = widget[eventOptions.handler]) === null || _widget$eventOptions$ === void 0 ? void 0 : _widget$eventOptions$.call(widget, info, event);
      }
    }
  }

}
//# sourceMappingURL=widget-manager.js.map