/**
 * @module meteoJS/modelviewer/container
 */
import Unique from '../base/Unique.js';
import addEventFunctions from '../Events.js';
import Resource from './Resource.js';
import Node from './Node.js';
import Display from './Display.js';
import Variable from './Variable.js';
import VariableCollection from './VariableCollection.js';

/**
 * Triggered, when visible Resource changes.
 * 
 * @event module:meteoJS/modelviewer/container#change:enabledResources
 * @type {Object}
 * @property {Object} options - Options.
 * @property {Map.<integer,module:meteoJS/modelviewer/resource.Resource>}
 *   [options.enabledResources] - Enabled resources, selected by selectedVariables.
 */

/**
 * Triggered, when visible Resource changes.
 * 
 * @event module:meteoJS/modelviewer/container#change:visibleResource
 */

/**
 * Triggered, when displayVariables is changed.
 * 
 * @event module:meteoJS/modelviewer/container#change:displayVariables
 * @type {Object}
 * @property {Object} options - Options.
 * @property {Set.<module:meteoJS/modelviewer/variable.Variable>}
 *   [options.addedVariables] - Added variables to displayVariables.
 * @property {Set.<module:meteoJS/modelviewer/variable.Variable>}
 *   [options.removedVariables] - Removed variables to displayVariables.
 */

/**
 * Triggered, when selectedVariables is changed.
 * 
 * @event module:meteoJS/modelviewer/container#change:selectedVariables
 * @type {Object}
 * @property {Object} options - Options.
 * @property {Set.<module:meteoJS/modelviewer/variable.Variable>}
 *   [options.addedVariables] - Added variables to selectedVariables.
 * @property {Set.<module:meteoJS/modelviewer/variable.Variable>}
 *   [options.removedVariables] - Removed variables to selectedVariables.
 */

/**
 * If a suitable resource is searched, this method will be called several times.
 * The first argument ist a list of variables. These variables are collected of
 * one hierarchy level, defined by the
 * {@link module:meteoJS/modelviewer/resources.Resources|resources object}.
 * The method returns an ordered list of these passed variables (or a subset).
 * For these variables, further down in the hierarchy, a possible resource is
 * searched. If one is found, this variable will be used for selectedVariables.
 * Method is only used if adaptSuitableResource is enabled. Default algorythm is
 * to return the list in the order of the Iterator.
 * 
 * @typedef {Function} module:meteoJS/modelviewer/container~getPossibleVariables
 * @param {module:meteoJS/modelviewer/variable.Variable[]}
 *   possibleSelectedVariables - Variables to return an ordered list.
 * @param {Set<module:meteoJS/modelviewer/variable.Variable>}
 *   selectedVariables - Already selected variables so far, top-down in
 *   hierarchy.
 * @returns {module:meteoJS/modelviewer/variable.Variable[]} - Ordered list.
 */

/**
 * With the passed selectedVariables, the method determines if already a
 * suitable resource should be selected. If method returns true, the property
 * selectedVariables will be set by the passed Set.
 * Method used if adaptSuitableResource is enabled. Default algorythm is to
 * return true if the Node of the lastAddedVariable contains resources.
 * 
 * @typedef {Function} module:meteoJS/modelviewer/container~isResourceSelected
 * @param {Set<module:meteoJS/modelviewer/variable.Variable>}
 *   selectedVariables - Selected variables so far.
 * @param {undefined|module:meteoJS/modelviewer/variable.Variable}
 *   lastAddedVariable - Last added variable to selectedVariables.
 * @returns {boolean} - True if a suitable resource should be selected with the
 *   current state of selectedVariables.
 */

/**
 * Options to adapt a suitable resource to display.
 * 
 * @typedef {Object}
 *   module:meteoJS/modelviewer/container~adaptSuitableResource
 * @param {boolean} enabled - Enabled adapt suitable resource.
 * @param {module:meteoJS/modelviewer/container~getPossibleVariables}
 *   getPossibleVariables - Determines order of variables of a hierarchy level.
 * @param {module:meteoJS/modelviewer/container~isResourceSelected}
 *   isResourceSelected - Is selectedVariables complete.
 */

/**
 * Options for constructor.
 * 
 * @typedef {module:meteoJS/base/unique~options}
 *   module:meteoJS/modelviewer/container~options
 * @param {module:meteoJS/modelviewer/display.Display} [display]
 *   Display object to output the container content to DOM.
 * @param {module:meteoJS/modelviewer/container~adaptSuitableResource}
 *   [adaptSuitableResource] - Options for adapt suitable resource.
 */

/**
 * This object represents a container, that displays one resource.
 * Via displayVariables the appropriate resource is chosen.
 * 
 * @extends module:meteoJS/base/unique.Unique
 * @fires module:meteoJS/modelviewer/container#change:visibleResource
 * @fires module:meteoJS/modelviewer/container#change:enabledResources
 * @fires module:meteoJS/modelviewer/container#change:displayVariables
 * @fires module:meteoJS/modelviewer/container#change:selectedVariables
 */
export class Container extends Unique {

  /**
   * @param {module:meteoJS/modelviewer/container~options} [options] - Options.
   */
  constructor({
    id,
    display = undefined,
    adaptSuitableResource = {}
  } = {}) {
    super({
      id
    });
    
    /**
     * @type undefined|module:meteoJS/modelviewer/display.Display
     * @private
     */
    this._display = (display === undefined) ? new Display() : display;
    this._display.modelviewer = this.modelviewer;
    this._display.container = this;
    this._display.parentNode = this._containerNode;
    
    /**
     * @type module:meteoJS/modelviewer/container~adaptSuitableResource
     * @private
     */
    this._adaptSuitableResource = {};
    this._initAdaptSuitableResource(adaptSuitableResource);
    
    /**
     * @type undefined|module:meteoJS/modelviewer.Modelviewer
     * @private
     */
    this._modelviewer = undefined;
    
    /**
     * @type undefined|module:meteoJS/modelviewer/resource.Resource
     * @private
     */
    this._visibleResource;
    
    /**
     * @type Set<module:meteoJS/modelviewer/variable.Variable>
     * @private
     */
    this._displayVariables = new Set();
    
    /**
     * @type Map.<module:meteoJS/modelviewer/variableCollection.VariableCollection,module:meteoJS/modelviewer/variable.Variable>
     * @private
     */
    this._selectedVariables = new Map();
    
    /**
     * @type module:meteoJS/modelviewer/node.Node|undefined
     * @private
     */
    this._selectedNode = undefined;
    
    /**
     * @type Map<integer,module:meteoJS/modelviewer/resource.Resource>
     * @private
     */
    this._enabledResources = new Map();
    
    /**
     * @type undefined|external:HTMLElement
     * @private
     */
    this._containerNode = undefined;

    /**
     * Function to call change:selectedVariables debouncec.
     * 
     * @type Function
     * @private
     */
    this._debouncedChangeSelectedVariables = (() => {
      let timeoutId;
      let totalAddedVariables = new Set();
      let totalRemovedVariables = new Set();
      return ({ addedVariables, removedVariables }) => {
        for (const v of addedVariables)
          if (totalRemovedVariables.has(v))
            totalRemovedVariables.delete(v);
        for (const v of removedVariables)
          if (totalAddedVariables.has(v))
            totalAddedVariables.delete(v);
        totalAddedVariables = new Set([...totalAddedVariables, ...addedVariables]);
        totalRemovedVariables = new Set([...totalRemovedVariables, ...removedVariables]);
        /*console.log([
          [...addedVariables].map(v => v.id),
          [...removedVariables].map(v => v.id),
          [...totalAddedVariables].map(v => v.id),
          [...totalRemovedVariables].map(v => v.id),
        ]);*/
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => {
          this.trigger('change:selectedVariables', {
            addedVariables: totalAddedVariables,
            removedVariables: totalRemovedVariables
          });
          totalAddedVariables.clear();
          totalRemovedVariables.clear();
        }, 300);
      };
    })();
    
    /**
     * @type Object<string,Object<string,mixed>>
     * @private
     */
    this._listeners = {
      mirror: [],
      timeline: {
        timeline: undefined,
        listenerKey: undefined
      },
      resources: {
        resources: undefined,
        listenerKey: undefined
      }
    };
  }
  
  /**
   * Display object to generate dom output.
   * 
   * @type undefined|module:meteoJS/modelviewer/display.Display
   * @readonly
   */
  get display() {
    return this._display;
  }
  
  /**
   * This container belongs to this modelviewer object.
   * 
   * @type undefined|module:meteoJS/modelviewer.Modelviewer
   * @package
   */
  get modelviewer() {
    return this._modelviewer;
  }
  set modelviewer(modelviewer) {
    this._modelviewer = modelviewer;
    if (this._modelviewer === undefined) {
      if (this._listeners.timeline.listenerKey !== undefined)
        this._listeners.timeline.timeline
          .un('change:time', this._listeners.timeline.listenerKey);
      if (this._listeners.resources.listenerKey !== undefined)
        this._listeners.resources.resources
          .un('change:resources', this._listeners.resources.listenerKey);
      return;
    }
    this._display.modelviewer = modelviewer;
    
    this._listeners.timeline.timeline = this._modelviewer.timeline;
    this._listeners.timeline.listenerKey = this._modelviewer.timeline
      .on('change:time', () => this._setVisibleResource());
    this._listeners.resources.resources = this._modelviewer.resources;
    this._listeners.resources.listenerKey = this._modelviewer.resources
      .on('change:resources', () => {
        this._setTimes();
        this._setEnabledResources();
        this._updateSelectedVariables();
      });
    this._setTimes();
  }
  
  /**
   * DOM node to append container's output.
   * 
   * @type undefined|external:HTMLElement
   * @package
   */
  get containerNode() {
    return this._containerNode;
  }
  set containerNode(containerNode) {
    this._containerNode = containerNode;
    if (this._containerNode === undefined)
      return;
    this._display.parentNode = this._containerNode;
  }
  
  /**
   * Currently visible resource. Could be an empty resource.
   * 
   * @type module:meteoJS/modelviewer/resource.Resource
   * @readonly
   */
  get visibleResource() {
    return (this._visibleResource === undefined) ?
      new Resource() : this._visibleResource;
  }
  
  /**
   * These variables define, which resource is displayed.
   * If adaptSuitableResource is not enabled, then the displayed resource is
   * exactly defined by these variables (and additionally the datetime selected
   * by the timeline object). If adaptSuitableResource is enabled, then a
   * resource is displayed, that matches the variables but can be defined by
   * additional variables.
   * Setter allows Set or Array. Getter returns always Set.
   * 
   * @type Set<module:meteoJS/modelviewer/variable.Variable>
   */
  get displayVariables() {
    return this._displayVariables;
  }
  set displayVariables(variables) {
    let addedVariables = new Set();
    variables = new Set(variables);
    for (let variable of variables)
      if (!this._displayVariables.has(variable))
        addedVariables.add(variable);
    let removedVariables = new Set();
    for (let displayVariable of this.displayVariables)
      if (!variables.has(displayVariable))
        removedVariables.add(displayVariable);
    if (
      addedVariables.size > 0 ||
      removedVariables.size > 0
    ) {
      this._displayVariables = variables;
      this._updateSelectedVariables({
        addedVariables,
        removedVariables
      });
      this.trigger(
        'change:displayVariables',
        { addedVariables, removedVariables }
      );
    }
  }
  
  /**
   * These variables define excactly, which resource will be displayed. These
   * variables are retrieved from the available resources and displayVariables.
   * Together with the selected time in the timeline, the resource to display
   * is uniquely defined.
   * If adaptSuitableResource is not enabled, selectedVariables is equal to
   * displayVariables.
   * 
   * @type Set<module:meteoJS/modelviewer/variable.Variable>
   * @readonly
   */
  get selectedVariables() {
    return new Set([...this._selectedVariables.values()]);
  }

  /**
   * Returns the selected Variable of a VariableCollection. If no selected
   * variable exists, an empty Variable-Object will be returned.
   * 
   * @param {module:meteoJS/modelviewer/variableCollection.VariableCollection}
   *   variableCollection - VariableCollection.
   * @returns {module:meteoJS/modelviewer/variable.Variable}
   *   The selected Variable of the colleciton.
   */
  getSelectedVariable(variableCollection) {
    const result = this._selectedVariables.get(variableCollection);
    return (result === undefined) ? new Variable({ id: undefined }) : result;
  }
  
  /**
   * Returns an array of times (for the timeline). For all of these times, there
   * exists resources which match with the current displayVariables.
   * 
   * @type Date[]
   * @readonly
   */
  get enabledTimes() {
    return [...this._enabledResources.keys()]
      .filter(datetime => !isNaN(datetime))
      .map(datetime => new Date(datetime));
  }
  
  /**
   * Exchanges variables in displayVariables. The variable with the same
   * Collection will be exchanged. If none is found, the variable will be added.
   * 
   * @param {Set<module:meteoJS/modelviewer/variable.Variable>} variables
   *   Add these variables to the set of displayVariables.
   * @returns {module:meteoJS/modelviewer/container.Container} - This.
   * @fires module:meteoJS/modelviewer/container#change:displayVariables
   */
  exchangeDisplayVariable(variables) {
    let displayVariables = new Set(this.displayVariables);
    for (let variable of variables)
      for (let displayVariable of this.displayVariables)
        if (displayVariable.variableCollection ===
            variable.variableCollection) {
          displayVariables.delete(displayVariable);
          displayVariables.add(variable);
        }
    for (let variable of variables)
      if (!displayVariables.has(variable))
        displayVariables.add(variable);
    this.displayVariables = displayVariables;
    return this;
  }
  
  /**
   * Mirrors (parts of) the displayVariables form another container. With this
   * feature, e.g. in different containers can be viewed plots of different
   * models. If you change e.g. the field in the first container, all other
   * containers, that mirrors form this container, will also change the viewed
   * content. It is possible to mirror different VariableCollections from
   * different containers.
   * 
   * @param {module:meteoJS/modelviewer/container.Container} [container]
   *   Mirrors from this container.
   * @param {module:meteoJS/modelviewer/variableCollection.VariableCollection[]}
   *   [variableCollections] - The displayVariables of these VariableCollections
   *   are mirrored. If omitted, all VariableCollections are mirrored.
   */
  mirrorsFrom(container = undefined, variableCollections = undefined) {
    this._listeners.mirror =
      this._listeners.mirror.filter(mirrorConfig => {
        if (mirrorConfig.container === container
          || container === undefined) {
          mirrorConfig.container
            .un('change:displayVariables', mirrorConfig.listenerKey);
          return false;
        }
        return true;
      });
    if (container === undefined)
      return;
    if (variableCollections !== undefined
      && variableCollections.length < 1)
      return;
    if (variableCollections === undefined)
      variableCollections = this.modelviewer.resources.variableCollections;
    const onChangeDisplayVariables = () => {
      const newDisplayVariables = new Set();
      for (const variable of container.displayVariables)
        variableCollections.forEach(collection => {
          if (variable.variableCollection === collection)
            newDisplayVariables.add(variable);
        });
      this.exchangeDisplayVariable(newDisplayVariables);
    };
    const listenerKey = container
      .on('change:displayVariables', onChangeDisplayVariables);
    const mirrorConfig = {
      container,
      listenerKey,
      variableCollections
    };
    this._listeners.mirror.forEach(mC => {
      const newVariableCollection = [];
      mC.variableCollections.forEach(collection => {
        let isContained = false;
        variableCollections.forEach(variableCollection => {
          if (variableCollection === collection)
            isContained = true;
        });
        if (!isContained)
          newVariableCollection.push(collection);
      });
      if (newVariableCollection.length < mC.variableCollections.length)
        this.mirrorsFrom(mC.container, newVariableCollection);
    });
    this._listeners.mirror.push(mirrorConfig);
    onChangeDisplayVariables();
  }

  /**
   * Get all containers, from which this container mirrors some variables from.
   * As values of the returned Map-Object an array with the mirrored
   * VariableColletions is returned.
   * 
   * @returns {Map.<module:meteoJS/modelviewer/container.Container,module:meteoJS/modelviewer/variableCollection.VariableCollection[]>}
   */
  getMirrorsFrom() {
    const result = new Map();
    this._listeners.mirror.forEach(mirrorConfig => {
      result.set(mirrorConfig.container, mirrorConfig.variableCollections);
    });
    return result;
  }
  
  /**
   * Sets all available times in the timeline object for this container.
   * 
   * @private
   */
  _setTimes() {
    let [selectedVariables] =
      this._getSelectedVariablesWithResources(
        [this.modelviewer.resources.topNode],
        new Set(),
        undefined,
        selectedVariables => {
          let result = true;
          this.modelviewer.resources._timesVariableCollections.forEach(collection => {
            let contained = false;
            for (let selectedVariable of selectedVariables) {
              if (collection.contains(selectedVariable))
                contained = true;
            }
            if (!contained)
              result = false;
          });
          return result;
        }
      );
    
    if (selectedVariables === undefined)
      selectedVariables = new Set();
    
    const availableTimes = (selectedVariables.size == 0)
      ? []
      : this.modelviewer.resources
        .getTimesByVariables({ variables: selectedVariables });
    this.modelviewer.timeline.setTimesBySetID(this.id, availableTimes);
  }
  
  /**
   * Updates the selected variables, according to displayVariables.
   * 
   * @private
   */
  _updateSelectedVariables({
    addedVariables = undefined,
    removedVariables = undefined
  } = {}) {
    if (!this._adaptSuitableResource.enabled) {
      let selectedNode = undefined;
      const findFirstNodeWithVariable = node => {
        if (node.hasResourcesByVariables(true, ...this.displayVariables))
          selectedNode = node;
        else
          for (const childNode of node.children)
            findFirstNodeWithVariable(childNode);
      };
      findFirstNodeWithVariable(this.modelviewer.resources.topNode);
      this._setSelectedVariables(this.displayVariables, selectedNode);
      return;
    }

    let nodes = [];
    const sV = new Set();
    let lSV = undefined;
    if (addedVariables === undefined || removedVariables === undefined)
      nodes.push(this.modelviewer.resources.topNode);
    else {
      const findFirstNodeWithVariable = node => {
        let isFound = false;
        for (const variable of [...addedVariables, ...removedVariables]) {
          if (variable.variableCollection !== undefined
            && variable.variableCollection.node === node) {
            nodes.push(node);
            isFound = true;
            break;
          }
        }
        if (!isFound) {
          const tempSV = this.getSelectedVariable(node.variableCollection);
          if (tempSV.id !== undefined) {
            lSV = tempSV;
            sV.add(lSV);
            for (const childNode of node.children)
              findFirstNodeWithVariable(childNode);
          }
        }
      };
      findFirstNodeWithVariable(this.modelviewer.resources.topNode);
      nodes = nodes.filter((n,i,a) => i===a.indexOf(n));
      if (nodes.length < 1)
        nodes.push(this.modelviewer.resources.topNode);
    }
    let [selectedVariables, lastSelectedVariable] =
      this._getSelectedVariablesWithResources(
        nodes,
        sV,
        lSV
      );
    
    let node;
    if (selectedVariables === undefined) {
      selectedVariables = sV;
      node = (lSV !== undefined)
        ? lSV.variableCollection.node
        : new Node({ variableCollection: new VariableCollection() });
    }
    else
      node = lastSelectedVariable.variableCollection.node;
    this._setSelectedVariables(selectedVariables, node);
  }
  
  /**
   * @typedef result_getSelectedVariablesWithResources
   * @type {Array}
   * @property {undefined|Set<module:meteoJS/modelviewer/variable.Variable>} 0
   * @property {undefined|module:meteoJS/modelviewer/variable.Variable} 1
   */
  
  /**
   * 
   * 
   * @param {Set<module:meteoJS/modelviewer/node.Node>} nodes - Nodes to check.
   * @param {Set<module:meteoJS/modelviewer/variable.Variable>}
   *   selectedVariables - Selected Variables from top until this node.
   * @returns {result_getSelectedVariablesWithResources}
   *   Array with first element the SelectedVariables, second element the last
   *   selectedVariable (node most down in the tree).
   * @private
   */
  _getSelectedVariablesWithResources(
    nodes,
    selectedVariables,
    lastSelectedVariable,
    isResourceSelected = this._adaptSuitableResource.isResourceSelected
  ) {
    if (isResourceSelected.call(this, selectedVariables, lastSelectedVariable))
      return [selectedVariables, lastSelectedVariable];
    
    let result = [undefined, undefined];
    const checkPossibleVariable = possibleSelectedVariable => {
      let tempSelectedVariables = new Set(selectedVariables);
      tempSelectedVariables.add(possibleSelectedVariable);
      let [resultSelectedVariables, resultLastSelectedVariable] =
        this
          ._getSelectedVariablesWithResources(
            possibleSelectedVariable.variableCollection.node.children,
            tempSelectedVariables,
            possibleSelectedVariable,
            isResourceSelected
          );
      if (resultSelectedVariables !== undefined) {
        result[0] = resultSelectedVariables;
        result[1] = resultLastSelectedVariable;
        return;
      }
      
      let isOnlyTimesVariables = true;
      for (let selectedVariable of tempSelectedVariables) {
        let contained = false;
        this.modelviewer.resources._timesVariableCollections.forEach(collection => {
          if (collection.contains(selectedVariable))
            contained = true;
        });
        if (!contained)
          isOnlyTimesVariables = false;
      }
      if (isOnlyTimesVariables &&
          tempSelectedVariables.size == this.modelviewer.resources._timesVariableCollections.size) {
        result[0] = tempSelectedVariables;
        result[1] = possibleSelectedVariable;
      }
    };

    let availableSelectedVariables = [];
    for (let childNode of nodes) {
      if (this.modelviewer.resources.availableVariablesMap.has(childNode) &&
          this.modelviewer.resources.availableVariablesMap.get(childNode).size)
        for (const variable of childNode.variableCollection) {
          if (!this.modelviewer.resources
            .availableVariablesMap.get(childNode).has(variable))
            continue;
          if (this.displayVariables.has(variable))
            checkPossibleVariable(variable);
          else if (this._adaptSuitableResource.enabled)
            availableSelectedVariables.push(variable);
          if (result[0] !== undefined)
            break;
        }
      if (result[0] !== undefined)
        break;
    }
    if (result[0] !== undefined)
      return result;
    
    const possibleSelectedVariables = this._adaptSuitableResource
      .getPossibleVariables
      .call(this, availableSelectedVariables, selectedVariables);
    for (const variable of possibleSelectedVariables) {
      checkPossibleVariable(variable);
      if (result[0] !== undefined)
        break;
    }
    
    return result;
  }
  
  /**
   * @param {Set<module:meteoJS/modelviewer/variable.Variable>}
   *   selectedVariables - New selectedVariables.
   * @param {module:meteoJS/modelviewer/node.Node} selectedNode
   *   Selectes resources from this Node.
   * @private
   */
  _setSelectedVariables(selectedVariables, selectedNode) {
    let addedVariables = new Set();
    selectedVariables = new Set(selectedVariables);
    for (let variable of selectedVariables)
      if (!this.selectedVariables.has(variable))
        addedVariables.add(variable);
    let removedVariables = new Set();
    for (let selectedVariable of this.selectedVariables)
      if (!selectedVariables.has(selectedVariable))
        removedVariables.add(selectedVariable);
    if (
      addedVariables.size > 0 ||
      removedVariables.size > 0 ||
      this._selectedNode !== selectedNode
    ) {
      this._selectedVariables.clear();
      for (const variable of selectedVariables)
        this._selectedVariables.set(variable.variableCollection, variable);
      this._selectedNode = selectedNode;
      this._setTimes();
      this._setEnabledResources();
      this._debouncedChangeSelectedVariables({
        addedVariables,
        removedVariables
      });
    }
  }
  
  /**
   * Sets internally _enabledResources. These resources are selected by
   * selectedVariable. The visibleResource is determined from this resources.
   * 
   * @private
   */
  _setEnabledResources() {
    this._enabledResources.clear();
    if (this._selectedNode === undefined)
      return;
    if (this.selectedVariables.size != 0)
      this._selectedNode
        .getResourcesByVariables(true, ...this.selectedVariables)
        .filter(r => r.datetime && !isNaN(r.datetime.valueOf()))
        .forEach(r => this._enabledResources.set(r.datetime.valueOf(), r));
    this.modelviewer.timeline
      .setEnabledTimesBySetID(this.id, this.enabledTimes);
    this.trigger('change:enabledResources', this._enabledResources);
    this._setVisibleResource();
  }
  
  /**
   * Sets visible resource.
   * 
   * @private
   */
  _setVisibleResource() {
    let oldVisibleResource = this._visibleResource;
    let datetime = this.modelviewer.timeline.getSelectedTime().valueOf();
    if (this._enabledResources.has(datetime))
      this._visibleResource = this._enabledResources.get(datetime);
    else
      this._visibleResource = undefined;
    if (this._visibleResource !== oldVisibleResource)
      this.trigger('change:visibleResource');
  }
  
  /**
   * Inits private property _adaptSuitableResource.
   * 
   * @param {module:meteoJS/modelviewer/container~adaptSuitableResource}
   *   [adaptSuitableResource] - Adapt suitable resource.
   * @private
   */
  _initAdaptSuitableResource({ enabled = true,
    getPossibleVariables = undefined,
    isResourceSelected = undefined,
    //excludeVariableCollectionFromSimiliarDisplay = []
  } = {}) {
    this._adaptSuitableResource = {
      enabled,
      getPossibleVariables,
      isResourceSelected
    };
    
    if (this._adaptSuitableResource.getPossibleVariables === undefined)
      this._adaptSuitableResource.getPossibleVariables =
        availableSV => availableSV;
    if (this._adaptSuitableResource.isResourceSelected === undefined)
      this._adaptSuitableResource.isResourceSelected =
      (selectedVariables, lastAddedVariable) => {
        if (lastAddedVariable === undefined)
          return false;
        let resources = lastAddedVariable.variableCollection
          .node.getResourcesByVariables(true, ...selectedVariables);
        return resources.length > 0;
      };
  }
}
addEventFunctions(Container.prototype);
export default Container;