const config=require('config');

let currentInputs={};
let handlers=[];
let hud;

const toAngleMagnitude=(x, y) => {
	return {
		angle: ((Math.atan2(x, y)+2*Math.PI)%(2*Math.PI))/Math.PI,
		magnitude: Math.hypot(x, y)
	};
};

const handleAngleMagnitude=(x, y, threshold=0, fn=null) => {
	const {angle, magnitude}=toAngleMagnitude(x, y);

	if(magnitude>threshold) {
		let inputs=currentInputs;
		if(angle>.25 && angle <.75) inputs.right=true;
		else if(angle>.75 && angle<1.25) inputs.up=true;
		else if(angle>1.25 && angle<1.75) inputs.left=true;
		else inputs.down=true;

		if(fn) fn(angle, magnitude);
	}
};

const removeChild=(parent, child) => {
	if(child.parentNode==parent) parent.removeChild(child);
};

const handleCrosspad=(() => {
	const ns='http://www.w3.org/2000/svg';
	const cross=document.createElementNS(ns, 'svg');
	cross.classList.add('crosspadOverlay');
	cross.setAttribute('width', 1000);
	cross.setAttribute('height', 1000);
	let dr=document.createElementNS(ns, 'line');
	dr.setAttribute('x1', 0);
	dr.setAttribute('y1', 0);
	dr.setAttribute('x2', 1000);
	dr.setAttribute('y2', 1000);
	dr.setAttribute('stroke', 'black');
	cross.appendChild(dr);
	let dl=document.createElementNS(ns, 'line');
	dl.setAttribute('x1', 1000);
	dl.setAttribute('y1', 0);
	dl.setAttribute('x2', 0);
	dl.setAttribute('y2', 1000);
	dl.setAttribute('stroke', 'black');
	cross.appendChild(dl);

	let useOverlay=false;
	let enabled=false;
	const displayOverlay=() => {
		if(useOverlay && enabled) hud.appendChild(cross);
		else removeChild(hud, cross);
	};
	config.watchB('input.touchscreen.crosspad.overlay', (k, v) => {
		useOverlay=v;
		displayOverlay();
	});

	const fn=e =>
		handleAngleMagnitude(
			e.touches[0].clientX-window.innerWidth/2,
			e.touches[0].clientY-window.innerHeight/2,
			0,
			null
		);

	const init=() => {
		useOverlay=config.getB('input.touchscreen.crosspad.overlay');
		enabled=true;
		displayOverlay();
	};
	const fini=() => {
		enabled=false;
		displayOverlay();
	};

	return {
		touchstart: fn,
		touchmove: fn,
		init, fini
	};
})();

const handleKeyboard={
	keydown: e => {
		let inputs=currentInputs;
		if(e.key=='ArrowUp') inputs.up=true;
		else if(e.key=='ArrowDown') inputs.down=true;
		else if(e.key=='ArrowLeft') inputs.left=true;
		else if(e.key=='ArrowRight') inputs.right=true;
	}
};

const handleJoystick=(() => {
	let cvs=document.createElement('canvas');
	cvs.classList.add('joystickOverlay');
	let ctx=cvs.getContext('2d');
	let enabled=false;
	let useOverlay=false;
	let firstTouch=false;

	let center={
		x: 0,
		y: 0
	};
	let deadzone;

	const displayOverlay=() => {
		if(!enabled || !useOverlay || !firstTouch) return removeChild(hud, cvs);

		cvs.width=cvs.height=4*deadzone+120;
		hud.appendChild(cvs);
		ctx.clearRect(0, 0, cvs.width, cvs.width);

		ctx.strokeStyle='black';
		ctx.lineWidth=1;
		ctx.beginPath();
		const rad=2*deadzone+50;
		ctx.moveTo(rad*Math.cos(Math.PI/4)+cvs.width/2, rad*Math.sin(Math.PI/4)+cvs.width/2);
		ctx.lineTo(rad*Math.cos(Math.PI/4+Math.PI)+cvs.width/2, rad*Math.sin(Math.PI/4+Math.PI)+cvs.width/2);
		ctx.moveTo(rad*Math.cos(-Math.PI/4)+cvs.width/2, rad*Math.sin(-Math.PI/4)+cvs.width/2);
		ctx.lineTo(rad*Math.cos(-Math.PI/4+Math.PI)+cvs.width/2, rad*Math.sin(-Math.PI/4+Math.PI)+cvs.width/2);
		ctx.stroke();

		ctx.strokeStyle='gray';
		ctx.lineWidth=2;
		ctx.beginPath();
		ctx.ellipse(cvs.width/2, cvs.width/2, deadzone, deadzone, 0, 0, Math.PI*2);
		ctx.stroke();
		ctx.lineWidth=3;
		ctx.beginPath();
		ctx.ellipse(cvs.width/2, cvs.width/2, deadzone*2+50, deadzone*2+50, 0, 0, Math.PI*2);
		ctx.stroke();

		cvs.style.left=center.x+'px';
		cvs.style.top=center.y+'px';
	};

	const init=() => {
		enabled=true;
		deadzone=config.getN('input.touchscreen.joystick.deadzone');
		useOverlay=config.getB('input.touchscreen.joystick.overlay');
		displayOverlay();
	};
	const fini=() => {
		enabled=false;
		displayOverlay();
	};
	config.watchN('input.touchscreen.joystick.deadzone', (k, v) => {
		deadzone=v;
		displayOverlay();
	});
	config.watchB('input.touchscreen.joystick.overlay', (k, v) => {
		useOverlay=v;
		displayOverlay();
	});

	return {
		init, fini,
		touchstart: e => {
			center.x=e.touches[0].clientX;
			center.y=e.touches[0].clientY;
			firstTouch=true;
			displayOverlay();
		},
		touchmove: e =>
			handleAngleMagnitude(
				e.touches[0].clientX-center.x,
				e.touches[0].clientY-center.y,
				deadzone,
				null
			)
	}
})();

const handleSwipe=(() => {
	let center={
		x: 0,
		y: 0
	};
	let deadzone;

	let resetCenter=e => {
		center.x=e.touches[0].clientX;
		center.y=e.touches[0].clientY;
	};

	const init=() => {
		deadzone=config.getN('input.touchscreen.swipe.deadzone');
	};
	config.watchN('input.touchscreen.swipe.deadzone', (k, v) => {
		deadzone=v;
	});

	return {
		init,
		touchstart: resetCenter,
		touchmove: e =>
			handleAngleMagnitude(
				e.touches[0].clientX-center.x,
				e.touches[0].clientY-center.y,
				deadzone,
				() => resetCenter(e)
			)
	}
})();

const handleGamepads=(() => {
	let deadzone;

	const init=() => {
		deadzone=config.getN('input.touchscreen.swipe.deadzone');
	};
	config.watchN('input.touchscreen.swipe.deadzone', (k, v) => {
		deadzone=v;
	});

	return {
		init,
		frame: () => {
			const gp=navigator.getGamepads()[0];
			let inputs=currentInputs;
			if(!gp || !gp.axes) return;

			handleAngleMagnitude(
				gp.axes[0],
				gp.axes[1],
				deadzone,
				null
			);
		}
	};
})();

const handleEvent=(type, evt) => {
	for(let handler of handlers) {
		let fn=handler[type];
		if(fn) fn(evt);
	}
};

const enableHandler=handler => {
	if(!handlers.includes(handler)) {
		handlers.push(handler);
		if(handler.init) handler.init();
	}
};
const disableHandler=handler => {
	let idx=handlers.indexOf(handler);
	if(idx!=-1) {
		handlers.splice(idx, 1);
		if(handler.fini) handler.fini();
	}
};
const linkHandler=(handler, key) => {
	if(config.getB(key)) enableHandler(handler);
	config.watchB(key, (k, v) => {
		if(v) enableHandler(handler);
		else disableHandler(handler);
	});
};

const init=({hud: hudElem}) => {
	hud=hudElem;
	linkHandler(handleCrosspad, 'input.touchscreen.crosspad.enabled');
	linkHandler(handleJoystick, 'input.touchscreen.joystick.enabled');
	linkHandler(handleSwipe, 'input.touchscreen.swipe.enabled');
	linkHandler(handleGamepads, 'input.gamepad.enabled');
	linkHandler(handleKeyboard, 'input.keyboard.enabled');
};

const clear=() =>
	Object
		.keys(currentInputs)
		.forEach(key => delete currentInputs[key]);

for(let type of ['keydown', 'touchstart', 'touchmove']) {
	window.addEventListener(type, handleEvent.bind(null, type));
}

return module.exports={
	inputs: currentInputs,
	clear,
	framefn: handleEvent.bind(null, 'frame'),
	init
};
