import macro from 'vtk.js/Sources/macro';

const { vtkErrorMacro } = macro;

function notImplemented(method) {
  return () => vtkErrorMacro(`vtkViewport::${method} - NOT IMPLEMENTED`);
}

// ----------------------------------------------------------------------------
// vtkViewport methods
// ----------------------------------------------------------------------------

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

  // Public API methods
  publicAPI.getViewProps = () => model.props;
  publicAPI.hasViewProp = (prop) =>
    !!model.props.filter((item) => item === prop).length;
  publicAPI.addViewProp = (prop) => {
    if (prop && !publicAPI.hasViewProp(prop)) {
      model.props = model.props.concat(prop);
    }
  };

  publicAPI.removeViewProp = (prop) => {
    const newPropList = model.props.filter((item) => item !== prop);
    if (model.props.length !== newPropList.length) {
      model.props = newPropList;
    }
  };

  publicAPI.removeAllViewProps = () => {
    model.props = [];
  };

  // this method get all the props including any nested props
  publicAPI.getViewPropsWithNestedProps = () => {
    model.allprops = model.props;
    for (let index = 0; index < model.props.length; ++index) {
      const prop = model.props[index];
      if (prop.getNestedProps()) {
        model.allprops = model.allprops.concat(prop.getNestedProps());
      }
    }
    return model.allprops;
  };

  publicAPI.addActor2D = publicAPI.addViewProp;
  publicAPI.removeActor2D = (prop) => {
    // VTK way: model.actors2D.RemoveItem(prop);
    publicAPI.removeViewProp(prop);
  };

  publicAPI.getActors2D = () => {
    model.actors2D = [];
    model.props.forEach((prop) => {
      model.actors2D = model.actors2D.concat(prop.getActors2D());
    });
    return model.actors2D;
  };

  publicAPI.displayToView = () =>
    vtkErrorMacro('call displayToView on your view instead');
  publicAPI.viewToDisplay = () =>
    vtkErrorMacro('callviewtodisplay on your view instead');
  publicAPI.getSize = () => vtkErrorMacro('call getSize on your View instead');

  publicAPI.normalizedDisplayToView = (x, y, z) => {
    // first to normalized viewport
    const nvp = publicAPI.normalizedDisplayToNormalizedViewport(x, y, z);

    // then to view
    return publicAPI.normalizedViewportToView(nvp[0], nvp[1], nvp[2]);
  };

  publicAPI.normalizedDisplayToNormalizedViewport = (x, y, z) => {
    const scale = [
      model.viewport[2] - model.viewport[0],
      model.viewport[3] - model.viewport[1],
    ];
    return [
      (x - model.viewport[0]) / scale[0],
      (y - model.viewport[1]) / scale[1],
      z,
    ];
  };

  publicAPI.normalizedViewportToView = (x, y, z) => [
    x * 2.0 - 1.0,
    y * 2.0 - 1.0,
    z * 2.0 - 1.0,
  ];

  publicAPI.viewToNormalizedDisplay = (x, y, z) => {
    // first to nvp
    const nvp = publicAPI.viewToNormalizedViewport(x, y, z);

    // then to ndp
    return publicAPI.normalizedViewportToNormalizedDisplay(
      nvp[0],
      nvp[1],
      nvp[2]
    );
  };

  publicAPI.normalizedViewportToNormalizedDisplay = (x, y, z) => {
    const scale = [
      model.viewport[2] - model.viewport[0],
      model.viewport[3] - model.viewport[1],
    ];
    return [
      x * scale[0] + model.viewport[0],
      y * scale[1] + model.viewport[1],
      z,
    ];
  };

  publicAPI.viewToNormalizedViewport = (x, y, z) => [
    (x + 1.0) * 0.5,
    (y + 1.0) * 0.5,
    (z + 1.0) * 0.5,
  ];

  publicAPI.PickPropFrom = notImplemented('PickPropFrom');
}

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

const DEFAULT_VALUES = {
  vtkWindow: null,
  background: [0, 0, 0],
  background2: [0.2, 0.2, 0.2],
  gradientBackground: false,
  viewport: [0, 0, 1, 1],
  aspect: [1, 1],
  pixelAspect: [1, 1],
  props: [],
  actors2D: [],
};

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

export function extend(publicAPI, model, initialValues = {}) {
  Object.assign(model, DEFAULT_VALUES, initialValues);

  // Build VTK API
  macro.obj(publicAPI, model);
  macro.event(publicAPI, model, 'event');

  macro.setGetArray(publicAPI, model, ['viewport'], 4);

  macro.setGetArray(publicAPI, model, ['background', 'background2'], 3);

  vtkViewport(publicAPI, model);
}

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

export const newInstance = macro.newInstance(extend, 'vtkViewport');

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

export default { newInstance, extend };

@@ -1127,90 +1127,143 @@
 s%0A  
-publicAPI.getViewPropsWithNestedProps = () =%3E %7B%0A    model.allprops = model.p
+function gatherProps(prop, allProps = %5B%5D) %7B%0A    allProps.push(prop);%0A    const children = prop.getNestedP
 rops
+()
 ;%0A
+    if (children) %7B%0A  
     
@@ -1276,37 +1276,26 @@
 et i
-ndex
  = 0; i
-ndex %3C model.props
+ %3C children
 .len
@@ -1303,15 +1303,11 @@
 th; 
+i
 ++
-index
 ) %7B%0A
@@ -1316,154 +1316,266 @@
     
-const prop = model.props%5Bindex%5D;%0A      if (prop.getNestedProps()) %7B%0A        model.allp
+  gatherProps(children%5Bi%5D, allProps);%0A      %7D%0A    %7D%0A    return allProps;%0A  %7D%0A%0A  publicAPI.getViewPropsWithNestedProps = () =%3E %7B%0A    const allP
 rops
- =
+Array = %5B%5D;%0A    for (let i = 0; i %3C
  model.
-all
 props.
-concat(prop.getNestedProps());%0A      %7D
+length; i++) %7B%0A      gatherProps(model.props%5Bi%5D, allPropsArray);
 %0A   
@@ -1592,22 +1592,21 @@
 urn 
-model.
 all
-p
+P
 rops
+Array
 ;%0A  

Handle recursively nested props