class Cable {
	start = "";
	end = "";
	strPath = "";
	color = "";
	strokeWidth = 10;
	bufferSize = 20;
	buffer = [];
	path = null;

	constructor(start) {
		this.start = start;
		this.randomColor();
	}

	randomColor() {
		const hue = Math.floor(Math.random() * 360);
		const saturation = Math.floor(50 + Math.random() * (50 + 1)) + "%";
		const lightness = "75%";
		this.color = "hsl(" + hue + ", " + saturation + ", " + lightness + ")";
	}

	createCable(position) {
		this.path = document.createElementNS("http://www.w3.org/2000/svg", "path");

		this.path.setAttribute("fill", "none");

		this.path.setAttribute("stroke", this.color);
		this.path.setAttribute("stroke-width", this.strokeWidth);
		this.path.setAttribute("stroke-linecap", "round");

		this.buffer = [];
		this.appendToBuffer(position);
		this.strPath = "M" + position.x + " " + position.y;
		this.path.setAttribute("d", this.strPath);
	}

	appendToBuffer(position) {
		this.buffer.push(position);
		while (this.buffer.length > this.bufferSize) {
			this.buffer.shift();
		}
	}

	getAveragePoint(offset) {
		// Calculate the average point, starting at offset in the buffer
		let len = this.buffer.length;
		if (len % 2 === 1 || len >= this.bufferSize) {
			let totalX = 0;
			let totalY = 0;
			let position, i;
			let count = 0;
			for (i = offset; i < len; i++) {
				count++;
				position = this.buffer[i];
				totalX += position.x;
				totalY += position.y;
			}
			return {
				x: totalX / count,
				y: totalY / count,
			};
		}
		return null;
	}

	updatePath() {
		let position = this.getAveragePoint(0);
		if (position) {
			// Get the smoothed part of the path that will not change
			this.strPath += " L" + position.x + " " + position.y;

			// Get the last part of the path (close to the current mouse position)
			// This part will change if the mouse moves again
			var tmpPath = "";
			for (var offset = 2; offset < this.buffer.length; offset += 2) {
				position = this.getAveragePoint(offset);
				tmpPath += " L" + position.x + " " + position.y;
			}
			this.path.setAttribute("d", this.strPath + tmpPath);
		}
	}
}

class Panel {
	width = 0;
	height = 0;

	svg = null;
	container = null;
	containerBoundingClient = null;

	model = {};
	params = [];
	sockets = [];

	cable = null;
	cables = null;

	constructor(svg, container, debug = false) {
		this.svg = this.htmlToElement(svg);
		this.container = container;
		this.container.style.position = "relative";
		this.containerRect = container.getBoundingClientRect();
		this.debug = debug;

		while (container.firstChild) {
			container.removeChild(container.lastChild);
		}
		container.appendChild(this.svg);

		this.setSize();
		this.createParams();
		this.createSockets();
		this.createCables();

		this.svg.addEventListener("mousedown", (e) => this.startCable(e));
		this.svg.addEventListener("mousemove", (e) => this.drawCable(e));
		this.svg.addEventListener("mouseup", (e) => this.endCable(e));
		// this.svg.addEventListener("mouseleave", (e) => this.endCable(e));
	}

	htmlToElement(string) {
		var template = document.createElement("template");
		string = string.trim(); // Never return a text node of whitespace as the result
		template.innerHTML = string;
		return template.content.firstChild;
	}

	setSize() {
		this.width = this.svg.getAttribute("width");
		this.height = this.svg.getAttribute("height");
		this.container.style.width = this.width + "px";
		this.container.style.height = this.height + "px";
	}

	createParams() {
		// this.svg.querySelector("#params").style.visibility = "hidden";
		this.params = this.svg.querySelectorAll("#params [fill='#FF0000']");
		this.model.params = [];

		for (const param of this.params) {
			let rect = param.getBoundingClientRect();

			let control = document.createElement("input");
			control.setAttribute("type", "range");
			control.setAttribute("data-width", rect.width);
			control.setAttribute("data-height", rect.height);

			control.setAttribute("data-fgcolor", "white");
			control.classList.add("input-knob");
			control.style.position = "absolute";
			control.style.left = rect.left - this.containerRect.left + "px";
			control.style.top = rect.top - this.containerRect.top + "px";

			control.setAttribute("name", param.id);

			this.container.appendChild(control);

			this.model.params.push(param.id);

			if (this.debug) {
				let label = document.createElement("label");
				label.setAttribute("for", param.id);
				label.innerHTML = param.id;

				label.style.position = "absolute";
				label.style.left = rect.left - this.containerRect.left + "px";
				label.style.top = rect.top + rect.height + "px";
				label.style.fontSize = "1rem";
				label.style.backgroundColor = "red";
				label.style.color = "white";

				this.container.appendChild(label);
			}
		}
		let group = this.svg.querySelector("#params");
		if (group) group.style.display = "none";
	}

	createSockets() {
		// this.svg.querySelector("#sockets").style.visibility = "hidden";
		this.sockets = this.svg.querySelectorAll("#sockets [fill='#00FF00']");
		this.model.sockets = [];
		for (const socket of this.sockets) {
			let rect = socket.getBoundingClientRect();

			let input = document.createElement("input");
			input.setAttribute("name", socket.id);
			input.classList.add("socket");

			input.style.position = "absolute";
			input.style.left = rect.left - this.containerRect.left + "px";
			input.style.top = rect.top - this.containerRect.top + "px";
			input.style.width = rect.width + "px";
			input.style.height = rect.height + "px";

			input.style.border = "none";
			input.style.background = "none";
			input.style.opacity = 0;

			if (this.debug) {
				input.style.border = "1px solid #00FF00";
				input.style.opacity = 1;
			}

			input.style.cursor = "alias";

			input.addEventListener("mouseup", (e) => this.endCable(e));

			input.addEventListener("mousedown", (e) => {
				e.preventDefault();
				this.startCable(e);
				return false;
			});

			this.container.appendChild(input);

			this.model.sockets.push(socket.id);
		}

		let group = this.svg.querySelector("#sockets");
		if (group) group.style.display = "none";
	}

	createCables() {
		let cables = document.createElementNS("http://www.w3.org/2000/svg", "svg");
		cables.classList.add("cables");
		cables.setAttribute("width", this.width);
		cables.setAttribute("height", this.height);
		cables.setAttribute("viewBox", `0 0 ${this.width} ${this.height}`);
		cables.setAttribute("fill", "none");
		cables.style.pointerEvents = "none";
		cables.style.position = "absolute";
		cables.style.left = 0;
		cables.style.top = 0;
		cables.style.width = this.width;
		cables.style.height = this.height;

		this.cables = cables;
		this.container.appendChild(this.cables);
	}

	startCable(event) {
		let position = this.getMousePosition(event);
		let socket = event.target;

		if (socket && socket.classList.contains("socket")) {
			this.cable = new Cable(socket.getAttribute("name"));
			this.cable.createCable(position);
			let cable = this.cable.path;
			cable.style.pointerEvents = "stroke";

			cable.addEventListener("click", (e) => {
				console.log(e);
				cable.remove();
			});

			this.cables.appendChild(this.cable.path);

			for (const input of this.container.querySelectorAll(".input-knob")) {
				input.style.pointerEvents = "none";
			}
		}
	}

	drawCable(event) {
		if (this.cable) {
			this.cable.appendToBuffer(this.getMousePosition(event));
			this.cable.updatePath();
		}
	}

	endCable(event) {
		if (this.cable) {
			let socket = event.target;

			if (socket && socket.classList.contains("socket")) {
				this.cable.end = socket.getAttribute("name");

				// TODO: log cable
			} else {
				this.cable.path.remove();
			}

			for (const input of this.container.querySelectorAll(".input-knob")) {
				input.style.pointerEvents = "all";
			}

			this.cable = null;
		}
	}

	getMousePosition(e) {
		return {
			x: e.pageX - this.containerRect.left,
			y: e.pageY - this.containerRect.top,
		};
	}
}
