import { app } from "../../scripts/app.js";
import { ComfyDialog, $el } from "../../scripts/ui.js";

// Adds the ability to save and add multiple nodes as a template
// To save:
// Select multiple nodes (ctrl + drag to select a region or ctrl+click individual nodes)
// Right click the canvas
// Save Node Template -> give it a name
//
// To add:
// Right click the canvas
// Node templates -> click the one to add
//
// To delete/rename:
// Right click the canvas
// Node templates -> Manage

const id = "Comfy.NodeTemplates";

class ManageTemplates extends ComfyDialog {
	constructor() {
		super();
		this.element.classList.add("comfy-manage-templates");
		this.templates = this.load();

		this.importInput = $el("input", {
			type: "file",
			accept: ".json",
			multiple: true,
			style: {display: "none"},
			parent: document.body,
			onchange: () => this.importAll(),
		});
	}

	createButtons() {
		const btns = super.createButtons();
		btns[0].textContent = "Cancel";
		btns.unshift(
			$el("button", {
				type: "button",
				textContent: "Save",
				onclick: () => this.save(),
			})
		);
		btns.unshift(
			$el("button", {
				type: "button",
				textContent: "Export",
				onclick: () => this.exportAll(),
			})
		);
		btns.unshift(
			$el("button", {
				type: "button",
				textContent: "Import",
				onclick: () => {
					this.importInput.click();
				},
			})
		);
		return btns;
	}

	load() {
		const templates = localStorage.getItem(id);
		if (templates) {
			return JSON.parse(templates);
		} else {
			return [];
		}
	}

	save() {
		// Find all visible inputs and save them as our new list
		const inputs = this.element.querySelectorAll("input");
		const updated = [];

		for (let i = 0; i < inputs.length; i++) {
			const input = inputs[i];
			if (input.parentElement.style.display !== "none") {
				const t = this.templates[i];
				t.name = input.value.trim() || input.getAttribute("data-name");
				updated.push(t);
			}
		}

		this.templates = updated;
		this.store();
		this.close();
	}

	store() {
		localStorage.setItem(id, JSON.stringify(this.templates));
	}

	async importAll() {
		for (const file of this.importInput.files) {
			if (file.type === "application/json" || file.name.endsWith(".json")) {
				const reader = new FileReader();
				reader.onload = async () => {
					var importFile = JSON.parse(reader.result);
					if (importFile && importFile?.templates) {
						for (const template of importFile.templates) {
							if (template?.name && template?.data) {
								this.templates.push(template);
							}
						}
						this.store();
					}
				};
				await reader.readAsText(file);
			}
		}

		this.importInput.value = null;

		this.close();
	}

	exportAll() {
		if (this.templates.length == 0) {
			alert("No templates to export.");
			return;
		}

		const json = JSON.stringify({templates: this.templates}, null, 2); // convert the data to a JSON string
		const blob = new Blob([json], {type: "application/json"});
		const url = URL.createObjectURL(blob);
		const a = $el("a", {
			href: url,
			download: "node_templates.json",
			style: {display: "none"},
			parent: document.body,
		});
		a.click();
		setTimeout(function () {
			a.remove();
			window.URL.revokeObjectURL(url);
		}, 0);
	}

	show() {
		// Show list of template names + delete button
		super.show(
			$el(
				"div",
				{
					style: {
						display: "grid",
						gridTemplateColumns: "1fr auto",
						gap: "5px",
					},
				},
				this.templates.flatMap((t) => {
					let nameInput;
					return [
						$el(
							"label",
							{
								textContent: "Name: ",
							},
							[
								$el("input", {
									value: t.name,
									dataset: { name: t.name },
									$: (el) => (nameInput = el),
								}),
							]
						),
						$el(
							"div",
							{},
							[
								$el("button", {
									textContent: "Export",
									style: {
										fontSize: "12px",
										fontWeight: "normal",
									},
									onclick: (e) => {
										const json = JSON.stringify({templates: [t]}, null, 2); // convert the data to a JSON string
										const blob = new Blob([json], {type: "application/json"});
										const url = URL.createObjectURL(blob);
										const a = $el("a", {
											href: url,
											download: (nameInput.value || t.name) + ".json",
											style: {display: "none"},
											parent: document.body,
										});
										a.click();
										setTimeout(function () {
											a.remove();
											window.URL.revokeObjectURL(url);
										}, 0);
									},
								}),
								$el("button", {
									textContent: "Delete",
									style: {
										fontSize: "12px",
										color: "red",
										fontWeight: "normal",
									},
									onclick: (e) => {
										nameInput.value = "";
										e.target.parentElement.style.display = "none";
										e.target.parentElement.previousElementSibling.style.display = "none";
									},
								}),
							]
						),
					];
				})
			)
		);
	}
}

app.registerExtension({
	name: id,
	setup() {
		const manage = new ManageTemplates();

		const clipboardAction = (cb) => {
			// We use the clipboard functions but dont want to overwrite the current user clipboard
			// Restore it after we've run our callback
			const old = localStorage.getItem("litegrapheditor_clipboard");
			cb();
			localStorage.setItem("litegrapheditor_clipboard", old);
		};

		const orig = LGraphCanvas.prototype.getCanvasMenuOptions;
		LGraphCanvas.prototype.getCanvasMenuOptions = function () {
			const options = orig.apply(this, arguments);

			options.push(null);
			options.push({
				content: `Save Selected as Template`,
				disabled: !Object.keys(app.canvas.selected_nodes || {}).length,
				callback: () => {
					const name = prompt("Enter name");
					if (!name || !name.trim()) return;

					clipboardAction(() => {
						app.canvas.copyToClipboard();
						manage.templates.push({
							name,
							data: localStorage.getItem("litegrapheditor_clipboard"),
						});
						manage.store();
					});
				},
			});

			// Map each template to a menu item
			const subItems = manage.templates.map((t) => ({
				content: t.name,
				callback: () => {
					clipboardAction(() => {
						localStorage.setItem("litegrapheditor_clipboard", t.data);
						app.canvas.pasteFromClipboard();
					});
				},
			}));

			subItems.push(null, {
				content: "Manage",
				callback: () => manage.show(),
			});

			options.push({
				content: "Node Templates",
				submenu: {
					options: subItems,
				},
			});

			return options;
		};
	},
});
