import * as BABYLON from 'babylonjs';
import { newVector } from './utilityHelpers';
import {
  scene,
  engine,
  canvas,
  selectedMeshes,
  getParamOfSelectedMeshes,
  getUserMeshes,
  defaultCameraPosition,
  getBoundingMeshData,
} from '../helpers';
import { CAMERAS, MESH_PARAMS } from '../constants';
import { focusCamera, toggleTurntable } from '../actions';
import { orthoOptions, ratios } from '../enums';
import _ from 'lodash';

export const newCamera = (name, location, type, meta = {}) => {
  const defaultName = `camera_${getSceneCameras().length + 1}`;

  if (type === CAMERAS.ArcRotateCamera) {
    return new BABYLON.ArcRotateCamera(
      name || defaultName,
      meta.alpha || 0,
      meta.beta || 0,
      meta.radius || 0,
      newVector(location.x, location.y, location.z),
      scene,
    );
  }

  if (CAMERAS[type] && typeof BABYLON[type] === 'function') {
    return new BABYLON[type](
      name || defaultName,
      newVector(location.x, location.y, location.z),
      scene,
    );
  }
};

export const prepareCamera = () => {
  const camera = scene.activeCamera;
  const canvasDimensions = engine?.getRenderingCanvasClientRect?.();
  const width = canvasDimensions?.width || ratios.square.value;
  const height = canvasDimensions?.height || ratios.square.value;
  const aspect = height / width;
  const userMeshes = getUserMeshes();

  camera.orthoLeft = -camera.radius;
  camera.orthoTop = camera.radius * aspect;
  camera.orthoRight = camera.radius;
  camera.orthoBottom = -camera.radius * aspect;
  camera.lowerRadiusLimit = 1;
  camera.upperRadiusLimit = 5000;
  camera.minZ = 0.1;

  camera.setPosition?.(defaultCameraPosition());
  camera.setTarget(BABYLON.Vector3.Zero());
  camera.attachControl(canvas, true);

  _.isEmpty(userMeshes) ? toggleTurntable(true) : focusCamera();
};

export const prepareMaterialCamera = () => {
  const camera = scene.activeCamera;
  const sphere = scene.getMeshById('node0');
  camera.position = defaultCameraPosition();

  if (sphere) {
    const primarySurfaces = [ sphere ];
    const meshCenter = getBoundingMeshData(primarySurfaces).center;

    camera.setTarget(meshCenter);
    camera.alpha = 1.57;
    camera.beta = 1.65;
    camera.lowerRadiusLimit = 0.3239;
    camera.upperRadiusLimit = 5000;
    camera.minZ = 0.1;
    camera.maxZ = 10000;
    camera.radius = 4;
  }
};

export const toggleCameraMode = (orthoCamera, requestedMode) => {
  const camera = scene.activeCamera;
  const babylonOrthographic = BABYLON.Camera.ORTHOGRAPHIC_CAMERA;
  const ratio = canvas.height / canvas.width;

  if (!camera) return;

  camera.mode = requestedMode;

  if (camera.mode === babylonOrthographic) {
    if (!_.isEmpty(selectedMeshes)) {
      camera.target = getParamOfSelectedMeshes(MESH_PARAMS.center);
    }

    camera.alpha = orthoOptions[orthoCamera].alpha;
    camera.beta = orthoOptions[orthoCamera].beta;

    // Zooming
    camera.lowerRadiusLimit = 5;
    camera.upperRadiusLimit = 5000;
    camera.minZ = 0.1;
    camera.maxZ = 10000;
    camera.wheelDeltaPercentage = 0.01;

    let oldRadius = camera.radius;

    scene.onBeforeRenderObservable.add(() => {
      if (oldRadius !== camera.radius) {
          const radiusChangeRatio = camera.radius / oldRadius;
          camera.orthoLeft *= radiusChangeRatio;
          camera.orthoRight *= radiusChangeRatio;
          oldRadius = camera.radius;
          camera.orthoTop = camera.orthoRight * ratio;
          camera.orthoBottom = camera.orthoLeft * ratio;
      }
    });
  }

  focusCamera?.();
};

export const getSceneCameras = () => scene.cameras;