import { m as macro } from '../../macros2.js';

const {
  vtkErrorMacro
} = macro;
const PASS_TYPES = ['Build', 'Render'];

// ----------------------------------------------------------------------------
// vtkViewNode methods
// ----------------------------------------------------------------------------

function vtkViewNode(publicAPI, model) {
  // Set our className
  model.classHierarchy.push('vtkViewNode');

  // Builds myself.
  publicAPI.build = prepass => {};

  // Renders myself
  publicAPI.render = prepass => {};
  publicAPI.traverse = renderPass => {
    // we can choose to do special
    // traversal here based on pass
    const passTraversal = renderPass.getTraverseOperation();
    const fn = publicAPI[passTraversal];
    if (fn) {
      fn(renderPass);
      return;
    }

    // default traversal
    publicAPI.apply(renderPass, true);
    for (let index = 0; index < model.children.length; index++) {
      model.children[index].traverse(renderPass);
    }
    publicAPI.apply(renderPass, false);
  };
  publicAPI.apply = (renderPass, prepass) => {
    const customRenderPass = publicAPI[renderPass.getOperation()];
    if (customRenderPass) {
      customRenderPass(prepass, renderPass);
    }
  };
  publicAPI.getViewNodeFor = dataObject => {
    if (model.renderable === dataObject) {
      return publicAPI;
    }
    for (let index = 0; index < model.children.length; ++index) {
      const child = model.children[index];
      const vn = child.getViewNodeFor(dataObject);
      if (vn) {
        return vn;
      }
    }
    return undefined;
  };
  publicAPI.getFirstAncestorOfType = type => {
    if (!model._parent) {
      return null;
    }
    if (model._parent.isA(type)) {
      return model._parent;
    }
    return model._parent.getFirstAncestorOfType(type);
  };
  publicAPI.getLastAncestorOfType = type => {
    if (!model._parent) {
      return null;
    }
    const lastAncestor = model._parent.getLastAncestorOfType(type);
    if (lastAncestor) {
      return lastAncestor;
    }
    if (model._parent.isA(type)) {
      return model._parent;
    }
    return null;
  };

  // add a missing node/child for the passed in renderables. This should
  // be called only in between prepareNodes and removeUnusedNodes
  publicAPI.addMissingNode = dobj => {
    if (!dobj) {
      return undefined;
    }

    // if found just mark as visited
    const result = model._renderableChildMap.get(dobj);
    if (result !== undefined) {
      result.setVisited(true);
      return result;
    }

    // otherwise create a node
    const newNode = publicAPI.createViewNode(dobj);
    if (newNode) {
      newNode.setParent(publicAPI);
      newNode.setVisited(true);
      model._renderableChildMap.set(dobj, newNode);
      model.children.push(newNode);
      return newNode;
    }
    return undefined;
  };

  // add missing nodes/children for the passed in renderables. This should
  // be called only in between prepareNodes and removeUnusedNodes
  publicAPI.addMissingNodes = dataObjs => {
    if (!dataObjs || !dataObjs.length) {
      return;
    }
    for (let index = 0; index < dataObjs.length; ++index) {
      const dobj = dataObjs[index];
      publicAPI.addMissingNode(dobj);
    }
  };

  // ability to add children that have no renderable use in the same manner
  // as addMissingNodes This case is when a normal viewnode wants to
  // delegate passes to a helper or child that doeasn't map to a clear
  // renderable or any renderable
  publicAPI.addMissingChildren = children => {
    if (!children || !children.length) {
      return;
    }
    for (let index = 0; index < children.length; ++index) {
      const child = children[index];
      const cindex = model.children.indexOf(child);
      if (cindex === -1) {
        child.setParent(publicAPI);
        model.children.push(child);
        const childRenderable = child.getRenderable();
        if (childRenderable) {
          model._renderableChildMap.set(childRenderable, child);
        }
      }
      child.setVisited(true);
    }
  };
  publicAPI.removeNode = child => {
    const childIdx = model.children.findIndex(x => x === child);
    if (childIdx < 0) {
      return false;
    }
    const renderable = child.getRenderable();
    if (renderable) {
      model._renderableChildMap.delete(renderable);
    }
    child.delete();
    model.children.splice(childIdx, 1);
    return true;
  };
  publicAPI.prepareNodes = () => {
    for (let index = 0; index < model.children.length; ++index) {
      model.children[index].setVisited(false);
    }
  };
  publicAPI.setVisited = val => {
    model.visited = val;
  };
  publicAPI.removeUnusedNodes = () => {
    let visitedCount = 0;
    for (let index = 0; index < model.children.length; ++index) {
      const child = model.children[index];
      const visited = child.getVisited();
      if (visited) {
        model.children[visitedCount++] = child;
        child.setVisited(false);
      } else {
        const renderable = child.getRenderable();
        if (renderable) {
          model._renderableChildMap.delete(renderable);
        }
        child.delete();
      }
    }
    model.children.length = visitedCount;
  };
  publicAPI.createViewNode = dataObj => {
    if (!model.myFactory) {
      vtkErrorMacro('Cannot create view nodes without my own factory');
      return null;
    }
    const ret = model.myFactory.createNode(dataObj);
    if (ret) {
      ret.setRenderable(dataObj);
    }
    return ret;
  };
  const parentDelete = publicAPI.delete;
  publicAPI.delete = () => {
    for (let i = 0; i < model.children.length; i++) {
      model.children[i].delete();
    }
    parentDelete();
  };
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
  // _parent: null,
  renderable: null,
  myFactory: null,
  children: [],
  visited: false
};

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

function extend(publicAPI, model) {
  let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  Object.assign(model, DEFAULT_VALUES, initialValues);

  // Build VTK API
  macro.obj(publicAPI, model);
  macro.event(publicAPI, model, 'event');
  model._renderableChildMap = new Map();
  macro.get(publicAPI, model, ['visited']);
  macro.setGet(publicAPI, model, ['_parent', 'renderable', 'myFactory']);
  macro.getArray(publicAPI, model, ['children']);
  macro.moveToProtected(publicAPI, model, ['parent']);

  // Object methods
  vtkViewNode(publicAPI, model);
}

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

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

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

var vtkViewNode$1 = {
  newInstance,
  extend,
  PASS_TYPES
};

export { vtkViewNode$1 as default };
