import { 
	PlaneGeometry, CylinderGeometry, CanvasTexture, Group, 
	Mesh, MeshStandardMaterial, MeshBasicMaterial, DoubleSide 
} from "three";

import { Text } from "troika-three-text";
class DashBoard {
	#points = 0;
	constructor(engine) {
		let svg = p=>`<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<g>
	<rect x="0" y="85%" width="100%" height="15%" opacity="0.73" fill="#98d696"/>
	<rect x="80%" y="0" width="20%" height="15%" opacity="0.73" fill="#98d696"/>
	<rect x="0" y="0" width="20%" height="15%" opacity="0.73" fill="#98d696" visibility="hidden"/>
	<circle r="10%" cx="11%" cy="85%" opacity="0.73" fill="#98d696" visibility="hidden"/>
</g>
<text id="hint" text-anchor="middle" x="50%" y="92%" font-family="MyriadPro-Regular, &apos;Myriad Pro&apos;" font-size="150%">${p.hint || ''}</text>
<text x="90%" text-anchor="middle" y="8%" font-family="MyriadPro-Regular, &apos;Myriad Pro&apos;" font-size="150%">Points</text>
</svg>`;
		
		let img = new Image(), url, levelProgress;
		let canvas = document.createElement('canvas');
		let ctx = canvas.getContext('2d');
		let texture = new CanvasTexture(canvas)
		let updating = false;
		let params = {}
		let occupied = false;
		
		img.addEventListener('load', function () {
			ctx.drawImage(img, 0, 0, engine.w, engine.h); 
			URL.revokeObjectURL(url);
			texture.needsUpdate = true;
			updating = false;
		})

		const dash = new Group(), hud = new Group(), hudTarget = new Group();
		hud.visible = false;
		let hudAnimation, hudPlane, textPlane;
		this.group = dash;
		dash.add(hud);
		hud.add(hudTarget)
		dash.visible = false;

		const dashGeometry = new PlaneGeometry(engine.aspect, 1);
		const dashMesh = new Mesh(dashGeometry, new MeshBasicMaterial({
			transparent: true,
			map: texture
		}))

		dash.add(dashMesh);
		engine.scene.add(dash);

		const loadingPlane = new Mesh(
			new PlaneGeometry(engine.aspect, 1), 
			new MeshStandardMaterial({
				color:0xffffff,
				opacity:0, transparent:true,
				roughness:0, metalness:0.1
			})
		);
		const loadingProgress = new ProgressBar(engine);
		loadingProgress.object.scale.set(engine.aspect*0.8, 0.05, 0.05)
		loadingProgress.object.position.set(-engine.aspect/2 + engine.aspect*0.1, 0, 0.1)
		loadingPlane.add(loadingProgress.object);
		dash.add(loadingPlane);

		(async()=>{
			hudPlane = new Mesh(
				new PlaneGeometry(engine.aspect, 1), 
				new MeshBasicMaterial({
					map: await engine.loadTexture('/static/textures/hud.png', ''),
					opacity: 0.37,
					transparent:true
				})
			);
			hudPlane.position.z = -0.22;
			hudPlane.position.y = -0.05
			hud.add(hudPlane)

			textPlane = new Mesh(
				new PlaneGeometry(engine.aspect, 0.17), 
				new MeshBasicMaterial({
					map: await engine.loadTexture('/static/textures/hud.png', ''),
					opacity: 0.52,
					transparent:true
				})
			);
			textPlane.position.z = -0.25;
			textPlane.position.y = -0.46
			textPlane.visible = false;
			dash.add(textPlane)
		})()

		const text = new Text()
		Object.assign(text, {
			text:``, 
			fontSize: 0.033, lineHeight: 1.1, maxWidth: engine.aspect * 0.8,
			textAlign: 'center', font: '/static/fonts/Montserrat-Regular.ttf',
			anchorX: 'center', anchorY: 0.03, depthOffset: 0.1, color: 0x000000,
			clipRect: [-engine.aspect * 0.4, -0.12, engine.aspect * 0.4, 0]
		})
		text.sync();
		text.position.y = -0.33;
		dash.add(text);

		const pointsText = new Text()
		Object.assign(pointsText, {
			text:``, fontSize: 0.044, font: '/static/fonts/Montserrat-Bold.ttf',
			outlineColor: 0x113377, outlineWidth: '3%', anchorX: 'center',
		})
		pointsText.position.set(0.86 * engine.aspect/2, 0.47, -0.001);
		dash.add(pointsText);

		engine.addEventListener('beforeRender', ()=>{
			dash.quaternion.copy(engine.camera.quaternion)
			dash.position.copy(engine.camera.position)
			dash.translateZ(-1.2 * engine.camera.zoom);
		})

		this.updateText = function(t, textScrolledCallback){
			textPlane.visible = !!t;
			engine.motionQueue.clear(text);
			text.text = t;
			text.anchorY = 0.03;
			text.sync(()=>{
				let dh = text.clipRect[1] - text.textRenderInfo.blockBounds[1];
				if (dh > 0){
					let updateFn = ()=>{
						//text.sync();
						if (text.textRenderInfo.blockBounds[1]> text.clipRect[1] * 1.33 ){
							textScrolledCallback?.(true);
							textScrolledCallback = null;
						}
					}
					engine.motionQueue.add({
						o: text,
						a: { anchorY: text.textRenderInfo.blockBounds[1] + 0.03 },
						t: dh*177,
						u: updateFn
					})
				}else{
					textScrolledCallback?.(false)
				}
			})
		}

		levelProgress = new ProgressBar(engine)
		dash.add(levelProgress.object);
		levelProgress.object.position.set(-engine.aspect/2 + engine.aspect/30, 0.45, -0.01)
		levelProgress.object.scale.set(engine.aspect/3, 0.02, 0.02)
		this.levelProgress = levelProgress;

		this.addPoints = function(p){
			this.#points += p;
			engine.motionQueue.add({
				o: pointsText,
				a: {rotation:{y: Math.PI}, scale:{x:1.5, y:1.5, z:1.5}},
				t: 0.25,
				f:()=>{
					pointsText.text = this.#points;
					pointsText.sync();
					engine.motionQueue.add({
						o: pointsText,
						a: {rotation:{y: Math.PI*2}, scale:{x:1, y:1, z:1}},
						t: 0.5,
						f: ()=>{pointsText.rotation.y = 0;}
					})
				}
			})
		}

		this.enable = ()=>{
			dash.visible = true;
		}

		this.disable = ()=>{
			dash.visible = false;
		}

		this.reset = ()=>{
			this.levelProgress.update(0);
			this.updateText('');
		}

		//dashPlacement, rotate = false, plane = false
		this.attach = (object, opts = {})=>{
			hud.visible = true;
			hudPlane.visible = !!opts.plane;
			if (opts.plane){
				hudPlane.scale.set(0, .1, 1);
				hudPlane.material.opacity = 0.5;
				engine.motionQueue.add([{
					o:hudPlane, a:{material:{opacity:0.73}}, t:.4, d:.8
				},{
					o:hudPlane, a:{scale:{x:1}}, t:.4
				},{
					o:hudPlane, a:{scale:{y:1}}, t:.4, d:.4,
				}])
			}
			if (occupied) return false;
			object._hud = {
				parent: object.parent,
				placement: {
					position: object.position.clone(),
					quaternion: object.quaternion.clone(),
					scale: object.scale.clone()
				}
			}
			hudTarget.attach(object);
			occupied = true;

			let result = new Promise((resolve, reject)=>{
				engine.motionQueue.add({
					o: object,
					a: opts.placement || { 
						quaternion: { x:0, y:0, z:0, w:0 },
						position: { x:0, y:0, z:0 },
						scale: { x: 1, y: 1, z: 1 }
					},
					t: opts.skipTransition ? 0 : 1,
					f: resolve
				})
			})

			if (opts.rotate){
				engine.motionQueue.add(hudAnimation = {
					o: hudTarget,
					a: { 
						rotation: { y: 2*Math.PI }
					},
					t: 4,
					r: true,
					d: 1
				})
			}
			return result;
		}

		this.detach = (object, opts = {})=>{
			engine.motionQueue.remove(hudAnimation);
			object._hud.parent?.attach(object);
			hud.rotation.y = 0;
			hud.visible = false;
			engine.motionQueue.add({
				o: object,
				a: object._hud.placement,
				t: opts.skipTransition ? 0 : 1
			});
			delete object._hud;
			occupied = false;
			hudAnimation = null;
		}

		this.loading = function(progress, tt){
			loadingPlane.visible = progress > 0 && progress < 1;
			loadingProgress.update(progress, tt)
		}
		this.loading(0,0);
	}

	get active(){
		return this.group.visible;
	}

	set active(v){
		this.group.visible = v;
	}

	get points(){
		return this.#points;
	}
}

class ProgressBar{
	constructor(engine, params = {}){
		this.object = new Group();
		const geometry = new CylinderGeometry( 0.5, 0.5, 1, 3, 1, false, 0, Math.PI ); 
		const staticCylinder = new Mesh( geometry, new MeshStandardMaterial({
			roughness: 0, metalness:0.1, transparent: true, opacity:0.52, color: 0x55ff00, side: DoubleSide
		}) ); 
		staticCylinder.rotation.set(-Math.PI/2, 0, Math.PI/2,)
		staticCylinder.position.x = 0.5;
		this.object.add( staticCylinder );

		const progressCylinder = new Mesh( geometry, new MeshStandardMaterial({
			roughness: 0, metalness:0.1, transparent: true, opacity:0.77, color: 0x11ff00
		}) ); 
		progressCylinder.rotation.set(Math.PI/2, 0, Math.PI/2,)
		this.object.add( progressCylinder );

		this.update = function(value, transitionTime = 0.5){
			progressCylinder.visible = !!value;
			engine.motionQueue.clear(progressCylinder);
			engine.motionQueue.add({
				o: progressCylinder,
				a: {
					scale: {y: value},
					position: {x: value / 2}
				},
				t: transitionTime
			})
		}
		
		this.update(0)
	}
}

export { DashBoard };