const express = require('express');
const { Bundle, Client } = require('node-osc');
const path = require('path');

const bodyParser = require('body-parser');
const app = express();

const oscClient = new Client('127.0.0.1', 3333);
let alive = [];
let fseq;

let objTriangle = [] ;

function getHypotenuse(touch1, touch2) {
	const x = Math.abs(touch1.x - touch2.x);
	const y = Math.abs(touch1.y - touch2.y);
	return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
}

function getTop(dotTrio) {
	const dist01 = getHypotenuse(dotTrio[0], dotTrio[1]);
	const dist02 = getHypotenuse(dotTrio[0], dotTrio[2]);
	const dist12 = getHypotenuse(dotTrio[1], dotTrio[2]);

	const diff01m02 = Math.abs(dist01 - dist02);
	const diff01m12 = Math.abs(dist01 - dist12);
	const diff02m12 = Math.abs(dist02 - dist12);

	if (diff01m02 < diff02m12 && diff01m02 < diff01m12) {return 0;}
	else if (diff01m12<diff01m02 && diff01m12<diff02m12) {return 1;}
	else if (diff02m12<diff01m02 && diff02m12<diff01m12) {return 2;}
}

function getAngleApex(dotTrio, topIndex) {
	let dotA;
	let dotB;
	let dotC;
	dotB = dotTrio[topIndex];
	if (topIndex == 0) {
		dotA = dotTrio[1];
		dotC = dotTrio[2];
	}
	else if (topIndex == 1) {
		dotA = dotTrio[0];
		dotC = dotTrio[2];
	}
	else if (topIndex == 2) {
		dotA = dotTrio[0];
		dotC = dotTrio[1];
	}

	const AB = [dotB.x - dotA.x, dotB.y - dotA.y] ;
	const CB = [dotB.x - dotC.x, dotB.y - dotC.y] ;

	const dotProd = (AB[0] * CB[0] + AB[1] * CB[1]);
	const crossProd = (AB[0]*CB[1] - AB[1] * CB[0]);

	const alpha = Math.atan2(crossProd, dotProd);
	//return alpha ;
	return Math.floor(alpha * 180. / Math.PI + 0.5) ;
}

function getOrientation(dotTrio, topIndex) {
	let dotA;
	let dotB;
	let dotC;
	dotB = dotTrio[topIndex];
	if (topIndex == 0) {
		dotA = dotTrio[1];
		dotC = dotTrio[2];
	}
	else if (topIndex == 1) {
		dotA = dotTrio[0];
		dotC = dotTrio[2];
	}
	else if (topIndex == 2) {
		dotA = dotTrio[0];
		dotC = dotTrio[1];
	}

	const middlePt = [(dotA.x+dotC.x)/2 ,(dotA.y+dotC.y)/2 ] ;
	let diff = [dotB.x - middlePt[0], dotB.y - middlePt[1]] ;
	const length = Math.sqrt(Math.pow(diff[0],2) + Math.pow(diff[1], 2) ) ;
	//normalize diff
	diff = [diff[0]/length, diff[1]/length];
	const rad = Math.atan2(diff[0], diff[1]) ;
 	return Math.floor( -1 * rad * 180 / Math.PI)  ;
	 //return length ;
}

const server = require('http').Server(app);

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}));
app.use(express.static('./frontend/assets'));

app.get('/', function (req, res) {
	res.sendFile('./frontend/index.html', {
		root: path.resolve(__dirname + '/..')
	})
});

app.post('/json', function (req, res) {
	if (req.body.debug) {
		//console.log(req.body);
	}
	let oscBundle;
	if (req.body.event === 'touchend') {
		fseq = fseq ? fseq + 1 : 1;

		const aliveMessage = [ '/tuio/2Dcur', 'alive' ].concat(alive);
		oscBundle = new Bundle(
			[ '/tuio/2Dcur', 'source', `tangibles${req.body.section.toString()}@127.0.0.1` ],
			aliveMessage,
			[ '/tuio/2Dcur', 'fseq', fseq ],
			[
				'/tuio/2Dcur',
				'del',
				req.body.changedTouches[0].identifier
			]
		);
		oscClient.send(oscBundle, () => {
			const index = alive.indexOf(req.body.changedTouches[0].identifier);
			alive.splice(index, 1);
			if (alive.length === 0) {
				oscBundle = new Bundle(
					[ '/tuio/2Dcur', 'source', `tangibles${req.body.section.toString()}@127.0.0.1` ],
					[ '/tuio/2Dcur', 'alive' ],
					[ '/tuio/2Dcur', 'fseq', fseq ]
				);
				oscClient.send(oscBundle, () => {
					res.status(200).send();
					fseq = 0;
				});
			} else {
				res.status(200).send();
			}
		});
	} else {
		if (req.body.changedTouches && req.body.changedTouches.length && req.body.changedTouches.length > 0) {
			fseq = fseq ? fseq + 1 : 1;
			const touches = Object.keys(req.body.changedTouches);
			const aliveMessage = [ '/tuio/2Dcur', 'alive' ].concat(alive);
			touches.forEach(touch => {
				const id = req.body.changedTouches[touch].identifier;
				if (!alive.includes(id)) {
					alive.push(id);
					aliveMessage.push(id);
				}
			});





			/* Listage de tous les points */
			const dots = [];
			touches.forEach(function(touch) {
				dots.push({
					id: req.body.changedTouches[touch].identifier,
					x: req.body.changedTouches[touch].clientX,
					y: req.body.changedTouches[touch].clientY
				});
			});
			// console.log(dots, dots.length);

			/* Listage des segments */
			const segments = [];
			if (dots.length > 2) {
				for (var i = 0; i < dots.length; i++) {
					for (var j = 0; j < dots.length; j++) {
						if (j !== i) {
							/* on vérifie que le segment n'est pas déjà listé */
							const alreadyExists = segments.find(segment => {
								return segment.identifiers.includes(i) && segment.identifiers.includes(j);
							});
							/* on calcule la taille du segment (l'hypoténuse) */
							var hyp = getHypotenuse(dots[i], dots[j]);
							/* on garde uniquement les segments inférieurs à 550px
							 * cette valeur devra être une variable de configuration */
							if (!alreadyExists && hyp <= 750) {
								segments.push({
									identifiers: [i, j],
									x1: dots[i].x,
									x2: dots[j].x,
									y1: dots[i].y,
									y2: dots[j].y,
									hyp
								});
							}
						}
					}
				}
			}
			// console.log(segments, segments.length);

			/* Listage des triangles */
			const triangles = [];
			/* on boucle sur les segments */
			segments.forEach((segment) => {
				const dot1 = segment.identifiers[0];
				const dot2 = segment.identifiers[1];
				/* on vérifie que le triangle n'est pas déjà listé */
				const alreadyExists = triangles.find(triangle => {
					return triangle.includes(dot1) && triangle.includes(dot2);
				});
				if (!alreadyExists) {
					/* on cherche les segments qui contiennent un des 2 points du segment actuel
					 * ex: si le segment actuel est AB, on cherche un segment contenant A (pour AC) et un autre contenant B (pour BC) */
					const found1 = segments.findIndex(seg => {
						return (seg.identifiers.includes(dot1) && !seg.identifiers.includes(dot2));
					});
					const found2 = segments.findIndex(seg => {
						return (seg.identifiers.includes(dot2) && !seg.identifiers.includes(dot1));
					});
					/* si on trouve bien les 2 segments (AC et BC), on peut créer un triangle */
					if (found1 !== -1 && found2 !== -1) {
						/* on devine quel est le 3ème point du triangle par rapport au segment actuel (le point C par rapport au segment AB) */
						const dot3 = segments[found1].identifiers.find(identifier => {
							return identifier !== dot1 && identifier !== dot2;
						});
						triangles.push([dot1, dot2, dot3]);
					}
				}
			});
			 console.log(triangles, triangles.length);

			//MOD TITI
			//objet pour stocker les informations des triangles identifiés (points, centre, apexAngle, orientation, indice apex, width, height)

			objTriangle = {} ;

			/* Définition de l'apex */
			triangles.forEach(triangle => {
				objTriangle.dots = [];
				objTriangle.dots[0] = dots[triangle[0]];
				objTriangle.dots[1] = dots[triangle[1]];
				objTriangle.dots[2] = dots[triangle[2]];

				objTriangle.apex = getTop(objTriangle.dots);
				objTriangle.center = [
					(objTriangle.dots[0].x+objTriangle.dots[1].x+objTriangle.dots[2].x)/3 ,
					(objTriangle.dots[0].y+objTriangle.dots[1].y+objTriangle.dots[2].y)/3
				];

				objTriangle.angleApex = getAngleApex(objTriangle.dots, objTriangle.apex) ;
				objTriangle.orientation = getOrientation(objTriangle.dots, objTriangle.apex) ;
				// /* on récupère les 3 côtés (segments) du triangle */
				// const segment1 = segments.find(segment => {
				// 	return segment.identifiers.includes(triangle[0]) && segment.identifiers.includes(triangle[1]);
				// });
				// const segment2 = segments.find(segment => {
				// 	return segment.identifiers.includes(triangle[1]) && segment.identifiers.includes(triangle[2]);
				// });
				// const segment3 = segments.find(segment => {
				// 	return segment.identifiers.includes(triangle[0]) && segment.identifiers.includes(triangle[2]);
				// });
				// /* on trouve quel côté est le plus court
				//  * NOTE: dans notre cas, l'apex sera toujours le point opposé au plus petit côté
				//  * cette méthode n'est pas généralisable à d'autres projets */
				// const smallestSegment = [segment1, segment2, segment3].reduce(function(prev, current) {
				// 	return (prev.hyp < current.hyp) ? prev : current
				// });

				// /* on déduit quel point du triangle est l'apex */
				// const apexDot = triangle.find(dot => {
				// 	return dot !== smallestSegment.identifiers[0] && dot !== smallestSegment.identifiers[1];
				// });
				console.log(objTriangle.apex);
				console.log('centerPos : ' + objTriangle.center + ' orientation : ' + objTriangle.orientation);

				/* Reste à faire:
				 * trouver la valeur de l'angle de l'apex (théorème des cosinus)
				 * trouver le "centre" du triangle (via les orthocentres ou médianes??)
				 * déduire l'orientation du triangle
				 */
			});


			//plante vite
			// if (objTriangle.dots != undefined){
			// 	let oscBundleObj ;
			// 	oscBundleObj = new Bundle(
			// 		[ '/tuio/2Dobj', 'source', `tangibles${req.body.section.toString()}@127.0.0.1` ],
			// 		[ '/tuio/2Dobj', 'alive', 1 ],
			// 		[
			// 			'/tuio/2Dobj',
			// 			'set',
			// 			1,
			// 			1,
			// 			objTriangle.center[0],
			// 			objTriangle.center[1],
			// 			objTriangle.orientation,
			// 			0.0,
			// 			0.0,
			// 			0.0,
			// 			0.0,
			// 			0.0
			// 		],
			// 		[ '/tuio/2Dobj', 'fseq', fseq ]
			// 	);
			// 	// 	// objTriangle.forEach(triangleIndex => {
			// 	// 	// 	oscBundle.append(
			// 	// 	// 		[
			// 	// 	// 			'/tuio/2Dobj',
			// 	// 	// 			'set',
			// 	// 	// 			triangleIndex,
			// 	// 	// 			triangleIndex,
			// 	// 	// 			objTriangle[triangleIndex].center[0],
			// 	// 	// 			objTriangle[triangleIndex].center[1],
			// 	// 	// 			objTriangle[triangleIndex].orientation,
			// 	// 	// 			0.0,
			// 	// 	// 			0.0,
			// 	// 	// 			0.0,
			// 	// 	// 			0.0,
			// 	// 	// 			0.0
			// 	// 	// 		]
			// 	// 	// 	);
			// 	// 	// });
			// 	console.log(oscBundleObj);
			// 	oscClient.send(oscBundleObj, () => {
			// 		res.status(200).send();
			// 	});
			// }


			// oscBundle = new Bundle(
			// 	[ '/tuio/2Dcur', 'source', `tangibles${req.body.section.toString()}@127.0.0.1` ],
			// 	aliveMessage,
			// 	[ '/tuio/2Dcur', 'fseq', fseq ]
			// );
			// touches.forEach(touch => {
			// 	oscBundle.append(
			// 		[
			// 			'/tuio/2Dcur',
			// 			'set',
			// 			req.body.changedTouches[touch].identifier,
			// 			req.body.changedTouches[touch].clientX / req.body.screenW,
			// 			req.body.changedTouches[touch].clientY / req.body.screenH,
			// 			0.0,
			// 			0.0
			// 		]
			// 	);
			// });
			oscBundle = new Bundle ;
			oscBundle.append([ '/tuio/2Dobj', 'alive', 1 ]);
			if (objTriangle.dots != undefined){
				oscBundle.append([
						'/tuio/2Dobj',
						'set',
						1,
						1,
						objTriangle.center[0],
						objTriangle.center[1],
						objTriangle.orientation,
						0.0,
						0.0,
						0.0,
						0.0,
						0.0
					]);
				}
				oscBundle.append([ '/tuio/2Dobj', 'fseq', fseq ]);
				oscClient.send(oscBundle, () => {
					res.status(200).send();
				});
			} else {
				res.status(400).send();
			}
	}
});

server.listen(5001, function () {
 console.log('Votre app est disponible sur localhost:5001 !')
});