import {BaseSopOperation} from './_Base';
import {CoreGroup} from '../../../core/geometry/Group';
import {MathUtils, Object3D, PerspectiveCamera, Vector3} from 'three';
import {InputCloneMode} from '../../../engine/poly/InputCloneMode';
import {DefaultOperationParams} from '../../../core/operations/_Base';
import {CameraNodeType} from '../../poly/NodeContext';
import {PERSPECTIVE_CAMERA_DEFAULT, registerPerspectiveCamera} from '../../../core/camera/CorePerspectiveCamera';
import {CameraAttribute, CORE_CAMERA_DEFAULT, PerspectiveCameraAttribute} from '../../../core/camera/CoreCamera';
import {isBooleanTrue} from '../../../core/Type';
import {CameraHelper} from '../../../core/helpers/CameraHelper';
import {ThreejsCoreObject} from '../../../core/geometry/modules/three/ThreejsCoreObject';
import type {BaseNodeType} from '../../nodes/_Base';
import {CoreSceneObjectsFactory, GeneratorName} from '../../../core/CoreSceneObjectsFactory';
interface CreatePerspectiveCameraParams {
	fov: number;
	near: number;
	far: number;
}
interface PerspectiveCameraSopParams extends CreatePerspectiveCameraParams, DefaultOperationParams {
	position: Vector3;
	rotation: Vector3;
	showHelper: boolean;
	matrixAutoUpdate: boolean;
	name: string;
	// PBR
	apertureBlades: number;
	fStop: number;
	focusDistance: number;
	apertureRotation: number;
	anamorphicRatio: number;
}
interface AttributeOptions {
	fov: number;
}

export class PerspectiveCameraSopOperation extends BaseSopOperation {
	static override readonly DEFAULT_PARAMS: PerspectiveCameraSopParams = {
		fov: PERSPECTIVE_CAMERA_DEFAULT.fov,
		near: CORE_CAMERA_DEFAULT.near,
		far: CORE_CAMERA_DEFAULT.far,
		position: new Vector3(0, 0, 0),
		rotation: new Vector3(0, 0, 0),
		showHelper: false,
		matrixAutoUpdate: true,
		name: CameraNodeType.PERSPECTIVE,
		// PBR
		apertureBlades: 6,
		fStop: 0.6,
		focusDistance: 10,
		apertureRotation: 0,
		anamorphicRatio: 1,
	};
	static override readonly INPUT_CLONED_STATE = InputCloneMode.NEVER;
	static override type(): Readonly<CameraNodeType.PERSPECTIVE> {
		return CameraNodeType.PERSPECTIVE;
	}
	static override onRegister = registerPerspectiveCamera;
	override cook(inputCoreGroups: CoreGroup[], params: PerspectiveCameraSopParams) {
		const camera = PerspectiveCameraSopOperation.createCamera(params, this._node);
		camera.name = params.name || CameraNodeType.PERSPECTIVE;

		camera.position.copy(params.position);
		camera.rotation.set(
			MathUtils.degToRad(params.rotation.x),
			MathUtils.degToRad(params.rotation.y),
			MathUtils.degToRad(params.rotation.z)
		);

		// this needs to be .updateWorldMatrix and not .updateMatrix
		// as otherwise the camera appears to behave find in most cases,
		// except when using the sop/cameraRenderScene
		camera.updateWorldMatrix(false, false);
		camera.updateProjectionMatrix();
		camera.matrixAutoUpdate = params.matrixAutoUpdate;

		PerspectiveCameraSopOperation.setCameraAttributes(camera, params);

		const pbrUpdateFunc = CoreSceneObjectsFactory.generator(GeneratorName.PERSPECTIVE_CAMERA_UPDATE);

		pbrUpdateFunc({
			camera,
			params: {
				apertureBlades: params.apertureBlades,
				fStop: params.fStop,
				focusDistance: params.focusDistance,
				apertureRotation: params.apertureRotation,
				anamorphicRatio: params.anamorphicRatio,
			},
		});

		const objects: Object3D[] = [camera];
		if (isBooleanTrue(params.showHelper)) {
			const helper = new CameraHelper(camera);
			helper.update();
			camera.add(helper);
		}

		return this.createCoreGroupFromObjects(objects);
	}
	static createCamera(params: CreatePerspectiveCameraParams, nodeGenerator?: BaseNodeType) {
		const camera = CoreSceneObjectsFactory.generator(GeneratorName.PERSPECTIVE_CAMERA)({
			fov: params.fov,
			aspect: 1,
			near: params.near,
			far: params.far,
		});
		if (nodeGenerator) {
			ThreejsCoreObject.addAttribute(camera, CameraAttribute.NODE_ID, nodeGenerator.graphNodeId());
		}
		return camera;
	}
	static setCameraAttributes(camera: PerspectiveCamera, options: AttributeOptions) {
		ThreejsCoreObject.addAttribute(camera, PerspectiveCameraAttribute.FOV, options.fov);
	}
}
