import { mat4, quat } from 'gl-matrix';
import { m as macro } from '../../macros2.js';
import vtkBoundingBox from '../../Common/DataModel/BoundingBox.js';
import { A as degreesFromRadians, r as radiansFromDegrees, a as areMatricesEqual } from '../../Common/Core/Math/index.js';
import vtkProp from './Prop.js';

const VTK_EPSILON = 1e-6;

// ----------------------------------------------------------------------------
// vtkProp3D methods
// ----------------------------------------------------------------------------

function vtkProp3D(publicAPI, model) {
  // Set our className
  model.classHierarchy.push('vtkProp3D');
  publicAPI.addPosition = deltaXYZ => {
    model.position = model.position.map((value, index) => value + deltaXYZ[index]);
    publicAPI.modified();
  };
  publicAPI.getOrientationWXYZ = () => {
    const q = quat.create();
    mat4.getRotation(q, model.rotation);
    const oaxis = new Float64Array(3);
    const w = quat.getAxisAngle(oaxis, q);
    return [degreesFromRadians(w), oaxis[0], oaxis[1], oaxis[2]];
  };
  publicAPI.getOrientationQuaternion = function () {
    let out = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
    return mat4.getRotation(out, model.rotation);
  };
  publicAPI.rotateX = val => {
    if (val === 0.0) {
      return;
    }
    mat4.rotateX(model.rotation, model.rotation, radiansFromDegrees(val));
    publicAPI.modified();
  };
  publicAPI.rotateY = val => {
    if (val === 0.0) {
      return;
    }
    mat4.rotateY(model.rotation, model.rotation, radiansFromDegrees(val));
    publicAPI.modified();
  };
  publicAPI.rotateZ = val => {
    if (val === 0.0) {
      return;
    }
    mat4.rotateZ(model.rotation, model.rotation, radiansFromDegrees(val));
    publicAPI.modified();
  };
  publicAPI.rotateWXYZ = (degrees, x, y, z) => {
    if (degrees === 0.0 || x === 0.0 && y === 0.0 && z === 0.0) {
      return;
    }

    // convert to radians
    const angle = radiansFromDegrees(degrees);
    const q = quat.create();
    quat.setAxisAngle(q, [x, y, z], angle);
    const quatMat = new Float64Array(16);
    mat4.fromQuat(quatMat, q);
    mat4.multiply(model.rotation, model.rotation, quatMat);
    publicAPI.modified();
  };
  publicAPI.rotateQuaternion = orientationQuaternion => {
    if (Math.abs(orientationQuaternion[3]) >= 1 - VTK_EPSILON) {
      return;
    }
    const oriQuatMat = mat4.fromQuat(new Float64Array(16), orientationQuaternion);
    mat4.multiply(model.rotation, model.rotation, oriQuatMat);
    publicAPI.modified();
  };
  publicAPI.setOrientation = (x, y, z) => {
    if (x === model.orientation[0] && y === model.orientation[1] && z === model.orientation[2]) {
      return false;
    }
    model.orientation = [x, y, z];
    mat4.identity(model.rotation);
    publicAPI.rotateZ(z);
    publicAPI.rotateX(x);
    publicAPI.rotateY(y);
    publicAPI.modified();
    return true;
  };
  publicAPI.setUserMatrix = matrix => {
    if (areMatricesEqual(model.userMatrix, matrix)) {
      return false;
    }
    mat4.copy(model.userMatrix, matrix);
    publicAPI.modified();
    return true;
  };
  publicAPI.getMatrix = () => {
    publicAPI.computeMatrix();
    return model.matrix;
  };
  publicAPI.computeMatrix = () => {
    // check whether or not need to rebuild the matrix
    if (publicAPI.getMTime() > model.matrixMTime.getMTime()) {
      mat4.identity(model.matrix);
      if (model.userMatrix) {
        mat4.multiply(model.matrix, model.matrix, model.userMatrix);
      }
      mat4.translate(model.matrix, model.matrix, model.origin);
      mat4.translate(model.matrix, model.matrix, model.position);
      mat4.multiply(model.matrix, model.matrix, model.rotation);
      mat4.scale(model.matrix, model.matrix, model.scale);
      mat4.translate(model.matrix, model.matrix, [-model.origin[0], -model.origin[1], -model.origin[2]]);
      mat4.transpose(model.matrix, model.matrix);

      // check for identity
      model.isIdentity = true;
      for (let i = 0; i < 4; ++i) {
        for (let j = 0; j < 4; ++j) {
          if ((i === j ? 1.0 : 0.0) !== model.matrix[i + j * 4]) {
            model.isIdentity = false;
          }
        }
      }
      model.matrixMTime.modified();
    }
  };
  publicAPI.getCenter = () => vtkBoundingBox.getCenter(model.bounds);
  publicAPI.getLength = () => vtkBoundingBox.getLength(model.bounds);
  publicAPI.getXRange = () => vtkBoundingBox.getXRange(model.bounds);
  publicAPI.getYRange = () => vtkBoundingBox.getYRange(model.bounds);
  publicAPI.getZRange = () => vtkBoundingBox.getZRange(model.bounds);
  publicAPI.getUserMatrix = () => model.userMatrix;
  function updateIdentityFlag() {
    publicAPI.computeMatrix();
  }
  publicAPI.onModified(updateIdentityFlag);
}

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

const DEFAULT_VALUES = {
  origin: [0, 0, 0],
  position: [0, 0, 0],
  orientation: [0, 0, 0],
  rotation: null,
  scale: [1, 1, 1],
  bounds: [1, -1, 1, -1, 1, -1],
  userMatrix: null,
  userMatrixMTime: null,
  cachedProp3D: null,
  isIdentity: true,
  matrixMTime: null
};

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

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

  // Inheritance
  vtkProp.extend(publicAPI, model, initialValues);
  model.matrixMTime = {};
  macro.obj(model.matrixMTime);

  // Build VTK API
  macro.get(publicAPI, model, ['bounds', 'isIdentity']);
  macro.getArray(publicAPI, model, ['orientation']);
  macro.setGetArray(publicAPI, model, ['origin', 'position', 'scale'], 3);

  // Object internal instance
  model.matrix = mat4.identity(new Float64Array(16));
  model.rotation = mat4.identity(new Float64Array(16));
  model.userMatrix = mat4.identity(new Float64Array(16));
  model.transform = null; // FIXME

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

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

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

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

var vtkProp3D$1 = {
  newInstance,
  extend
};

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