import * as RE from 'rogue-engine';
import * as THREE from 'three'
import RaycastReceiver from './RaycastReceiver.re';

export default class RaycastReporter extends RE.Component {
	// @RE.Prop("Select") something;
	// somethingOptions = ["One", "Two"]
	mouse: THREE.Vector2
	raycaster: THREE.Raycaster
	updated: boolean

	@RE.PropList("String") receiverClass = ["RaycastReceiver"]
	allHovered: Array<DataThing> = []

	awake() {

	}

	start() {
		this.mouse = new THREE.Vector2(0, 0)
		this.raycaster = new THREE.Raycaster()
		this.updated = false
		RE.Input.touch.enabled = true
	}

	normalizeScreenInput(browserVector, gameVector) {
		const bounds = RE.Runtime.rogueDOMContainer.getBoundingClientRect();
		gameVector.x = ((browserVector.x - bounds.left) / bounds.width) * 2 - 1;
		gameVector.y = -((browserVector.y - bounds.top) / bounds.height) * 2 + 1
	}

	getMouseInput() {
		if (!RE.Input.mouse.isMoving) {
			return
		}
		this.normalizeScreenInput(RE.Input.mouse, this.mouse)
		this.updated = true
	}

	getTouchInput() {
		if (RE.Input.touch.touches.length == 0) {
			return
		}

		this.normalizeScreenInput(RE.Input.touch.touches[0], this.mouse)
		this.updated = true

	}

	update() {
		this.updated = false
		this.getMouseInput()
		this.getTouchInput()

		if (!this.updated) {
			return
		}

		this.raycaster.setFromCamera(this.mouse, this.object3d as THREE.Camera)

		let intersects = this.raycaster.intersectObjects(RE.App.currentScene.children)

		let listeningComponents: Array<DataThing> = []

		intersects.forEach(intersect => {
			let object3d = RE.App.currentScene.getObjectByProperty('uuid', intersect.object.uuid)
			if (object3d == null) {
				console.error("Could not find object in current scene")
				return
			}
			let component = this.crawlSceneToFindReceiver(object3d)

			if (component == null) {
				return
			}

			listeningComponents = listeningComponents.filter((dataThing) => {
				if (component == null) {
					return
				}
				dataThing.component.object3d.uuid != component.object3d.uuid
			})
			listeningComponents.push(new DataThing(component, intersect))
		})

		//unhover all previously hovered components
		for (let i = 0; i < this.allHovered.length; i++) {
			let dataThing = this.allHovered[i]
			if (!listeningComponents.includes(dataThing)) {
				dataThing.component.onMouseOut()
			}
		}

		this.allHovered = []

		for (let i = 0; i < listeningComponents.length; i++) {
			let dataThing = listeningComponents[i]
			this.allHovered.push(dataThing)
			if (dataThing.component && dataThing.component.onMouseOver(dataThing.intersect) === false) {
				break;
			}
		}
	}

	crawlSceneToFindReceiver(object3d): RaycastReceiver | null {
		const component = RE.getComponent(RaycastReceiver, object3d)
		if (component != null) {
			return component
		}
		if (object3d.parent == null) {
			return null
		}
		return this.crawlSceneToFindReceiver(object3d.parent)
	}
}

class DataThing {
	component: RaycastReceiver
	intersect: THREE.Intersection
	constructor(component, intersect) {
		this.component = component
		this.intersect = intersect
	}
}

RE.registerComponent(RaycastReporter);