import { m as macro } from '../../macros2.js';
import vtkAbstractWidget from './AbstractWidget.js';
import { extractRenderingComponents } from './WidgetManager.js';

function NoOp() {}

// ----------------------------------------------------------------------------

function vtkAbstractWidgetFactory(publicAPI, model) {
  model.classHierarchy.push('vtkAbstractWidgetFactory');

  // DO NOT share on the model ------------------------------------------------
  const viewToWidget = {};
  // DO NOT share on the model ------------------------------------------------

  // Can be called with just ViewId after the widget has been registered
  publicAPI.getWidgetForView = _ref => {
    let {
      viewId,
      renderer,
      viewType,
      initialValues
    } = _ref;
    if (!viewToWidget[viewId]) {
      if (!renderer) {
        return null;
      }
      const {
        interactor,
        apiSpecificRenderWindow,
        camera
      } = extractRenderingComponents(renderer);
      const widgetModel = {};
      const widgetPublicAPI = {};
      macro.obj(widgetPublicAPI, widgetModel);
      Object.assign(widgetPublicAPI, {
        onWidgetChange: publicAPI.onWidgetChange
      });
      Object.assign(widgetModel, {
        widgetState: model.widgetState,
        manipulator: model.manipulator,
        viewType,
        renderer,
        camera,
        apiSpecificRenderWindow,
        factory: publicAPI
      });
      macro.moveToProtected(widgetPublicAPI, widgetModel, ['renderer', 'camera', 'apiSpecificRenderWindow', 'factory']);
      macro.get(widgetPublicAPI, widgetModel, ['viewType']);
      macro.safeArrays(widgetModel);
      vtkAbstractWidget.extend(widgetPublicAPI, widgetModel, initialValues);

      // Create representations for that view
      /* eslint-disable no-shadow */
      const widgetInitialValues = initialValues; // Avoid shadowing
      widgetModel.representations = publicAPI.getRepresentationsForViewType(viewType).map(_ref2 => {
        let {
          builder,
          labels,
          initialValues
        } = _ref2;
        return builder.newInstance({
          _parentProp: widgetPublicAPI,
          labels,
          ...initialValues,
          ...widgetInitialValues
        });
      });
      /* eslint-enable no-shadow */

      widgetModel.representations.forEach(r => {
        r.setInputData(widgetModel.widgetState);
        r.getActors().forEach(actor => {
          widgetModel.actorToRepresentationMap.set(actor, r);
        });
      });
      model.behavior(widgetPublicAPI, widgetModel);
      // Forward representation methods
      ['coincidentTopologyParameters', ...(model.methodsToLink || [])].forEach(methodName => {
        const set = `set${macro.capitalize(methodName)}`;
        const get = `get${macro.capitalize(methodName)}`;
        const methods = {
          [methodName]: [],
          [set]: [],
          [get]: []
        };
        widgetModel.representations.forEach(representation => {
          if (representation[methodName]) {
            methods[methodName].push(representation[methodName]);
          }
          if (representation[set]) {
            methods[set].push(representation[set]);
          }
          if (representation[get]) {
            methods[get].push(representation[get]);
          }
        });
        Object.keys(methods).forEach(name => {
          const calls = methods[name];
          if (calls.length === 1) {
            widgetPublicAPI[name] = calls[0];
          } else if (calls.length > 1) {
            widgetPublicAPI[name] = macro.chain(...calls);
          }
        });
      });

      // Custom delete to detach from parent
      widgetPublicAPI.delete = macro.chain(() => {
        delete viewToWidget[viewId];
      }, widgetPublicAPI.delete);
      widgetPublicAPI.setInteractor(interactor);
      const viewWidget = Object.freeze(widgetPublicAPI);
      viewToWidget[viewId] = viewWidget;
      return viewWidget;
    }
    return viewToWidget[viewId];
  };

  // List of all the views the widget has been registered to.
  publicAPI.getViewIds = () => Object.keys(viewToWidget);
  publicAPI.getViewWidgets = () => Object.values(viewToWidget);

  // --------------------------------------------------------------------------
  // Widget visibility / enable
  // --------------------------------------------------------------------------
  // Call methods on all its view widgets

  publicAPI.setVisibility = value => {
    const viewIds = Object.keys(viewToWidget);
    for (let i = 0; i < viewIds.length; i++) {
      viewToWidget[viewIds[i]].setVisibility(value);
    }
  };
  publicAPI.setPickable = value => {
    const viewIds = Object.keys(viewToWidget);
    for (let i = 0; i < viewIds.length; i++) {
      viewToWidget[viewIds[i]].setPickable(value);
    }
  };
  publicAPI.setDragable = value => {
    const viewIds = Object.keys(viewToWidget);
    for (let i = 0; i < viewIds.length; i++) {
      viewToWidget[viewIds[i]].setDragable(value);
    }
  };
  publicAPI.setContextVisibility = value => {
    const viewIds = Object.keys(viewToWidget);
    for (let i = 0; i < viewIds.length; i++) {
      viewToWidget[viewIds[i]].setContextVisibility(value);
    }
  };
  publicAPI.setHandleVisibility = value => {
    const viewIds = Object.keys(viewToWidget);
    for (let i = 0; i < viewIds.length; i++) {
      viewToWidget[viewIds[i]].setHandleVisibility(value);
    }
  };

  // --------------------------------------------------------------------------
  // Place Widget API
  // --------------------------------------------------------------------------

  publicAPI.placeWidget = bounds => model.widgetState.placeWidget(bounds);
  publicAPI.getPlaceFactor = () => model.widgetState.getPlaceFactor();
  publicAPI.setPlaceFactor = factor => model.widgetState.setPlaceFactor(factor);

  // --------------------------------------------------------------------------
  // Event Widget API
  // --------------------------------------------------------------------------
  let unsubscribe = NoOp;
  publicAPI.delete = macro.chain(publicAPI.delete, () => unsubscribe());
  if (model.widgetState) {
    unsubscribe = model.widgetState.onModified(() => publicAPI.invokeWidgetChange(model.widgetState)).unsubscribe;
  }
}

// ----------------------------------------------------------------------------

function extend(publicAPI, model) {
  let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  Object.assign(model, initialValues);
  macro.obj(publicAPI, model);
  macro.get(publicAPI, model, ['widgetState']);
  macro.event(publicAPI, model, 'WidgetChange');
  vtkAbstractWidgetFactory(publicAPI, model);
}

// ----------------------------------------------------------------------------

const newInstance = macro.newInstance(extend, 'vtkAbstractWidget');

// ----------------------------------------------------------------------------

var vtkAbstractWidgetFactory$1 = {
  newInstance,
  extend
};

export { vtkAbstractWidgetFactory$1 as default, extend, newInstance };
