var editorControlsLeft = document.getElementById("image-editor-controls-left")

const IMAGE_EDITOR_MAX_SIZE = 800

const IMAGE_EDITOR_BUTTONS = [
	{
		name: "Cancel",
		icon: "fa-regular fa-circle-xmark",
		handler: editor => {
			editor.hide()
		}
	},
	{
		name: "Save",
		icon: "fa-solid fa-floppy-disk",
		handler: editor => {
			editor.saveImage()
		}
	}
]

const defaultToolBegin = (editor, ctx, x, y, is_overlay = false) => {
	ctx.beginPath()
	ctx.moveTo(x, y)
}
const defaultToolMove = (editor, ctx, x, y, is_overlay = false) => {
	ctx.lineTo(x, y)
	if (is_overlay) {
		ctx.clearRect(0, 0, editor.width, editor.height)
		ctx.stroke()
	}
}
const defaultToolEnd = (editor, ctx, x, y, is_overlay = false) => {
	ctx.stroke()
	if (is_overlay) {
		ctx.clearRect(0, 0, editor.width, editor.height)
	}
}
const toolDoNothing = (editor, ctx, x, y, is_overlay = false) => {}

const IMAGE_EDITOR_TOOLS = [
	{
		id: "draw",
		name: "Draw",
		icon: "fa-solid fa-pencil",
		cursor: "url(/media/images/fa-pencil.svg) 0 24, pointer",
		begin: defaultToolBegin,
		move: defaultToolMove,
		end: defaultToolEnd
	},
	{
		id: "erase",
		name: "Erase",
		icon: "fa-solid fa-eraser",
		cursor: "url(/media/images/fa-eraser.svg) 0 14, pointer",
		begin: defaultToolBegin,
		move: (editor, ctx, x, y, is_overlay = false) => {
			ctx.lineTo(x, y)
			if (is_overlay) {
				ctx.clearRect(0, 0, editor.width, editor.height)
				ctx.globalCompositeOperation = "source-over"
				ctx.globalAlpha = 1
				ctx.filter = "none"
				ctx.drawImage(editor.canvas_current, 0, 0)
				editor.setBrush(editor.layers.overlay)
				ctx.stroke()
				editor.canvas_current.style.opacity = 0
			}
		},
		end: (editor, ctx, x, y, is_overlay = false) => {
			ctx.stroke()
			if (is_overlay) {
				ctx.clearRect(0, 0, editor.width, editor.height)
				editor.canvas_current.style.opacity = ""
			}
		},
		setBrush: (editor, layer) => {
			layer.ctx.globalCompositeOperation = "destination-out"
		}
	},
	{
		id: "fill",
		name: "Fill",
		icon: "fa-solid fa-fill",
		cursor: "url(/media/images/fa-fill.svg) 20 6, pointer",
		begin: (editor, ctx, x, y, is_overlay = false) => {
			if (!is_overlay) {
				var color = hexToRgb(ctx.fillStyle)
				color.a = parseInt(ctx.globalAlpha * 255) // layer.ctx.globalAlpha
				flood_fill(editor, ctx, parseInt(x), parseInt(y), color)
			}
		},
		move: toolDoNothing,
		end: toolDoNothing
	},
	{
		id: "colorpicker",
		name: "Picker",
		icon: "fa-solid fa-eye-dropper",
		cursor: "url(/media/images/fa-eye-dropper.svg) 0 24, pointer",
		begin: (editor, ctx, x, y, is_overlay = false) => {
			if (!is_overlay) {
				var img_rgb = editor.layers.background.ctx.getImageData(x, y, 1, 1).data
				var drawn_rgb = editor.ctx_current.getImageData(x, y, 1, 1).data
				var drawn_opacity = drawn_rgb[3] / 255
				editor.custom_color_input.value = rgbToHex({ 
					r: (drawn_rgb[0] * drawn_opacity) + (img_rgb[0] * (1 - drawn_opacity)),
					g: (drawn_rgb[1] * drawn_opacity) + (img_rgb[1] * (1 - drawn_opacity)),
					b: (drawn_rgb[2] * drawn_opacity) + (img_rgb[2] * (1 - drawn_opacity)),
				})
				editor.custom_color_input.dispatchEvent(new Event("change"))
			}
		},
		move: toolDoNothing,
		end: toolDoNothing
	}
]

const IMAGE_EDITOR_ACTIONS = [
	{
		id: "load_mask",
		name: "Load mask from file",
		className: "load_mask",
		icon: "fa-regular fa-folder-open",
		handler: (editor) => {
			let el = document.createElement('input')
			el.setAttribute("type", "file")
			el.addEventListener("change", function() {
				if (this.files.length === 0) {
					return
				}

				let reader = new FileReader()
				let file = this.files[0]

				reader.addEventListener('load', function(event) {
					let maskData = reader.result

					editor.layers.drawing.ctx.clearRect(0, 0, editor.width, editor.height)
					var image = new Image()
					image.onload = () => {
						editor.layers.drawing.ctx.drawImage(image, 0, 0, editor.width, editor.height)
					}
					image.src = maskData
				})

				if (file) {
					reader.readAsDataURL(file)
				}
			})

			el.click()
		},
		trackHistory: true
	},
	{
		id: "fill_all",
		name: "Fill all",
		icon: "fa-solid fa-paint-roller",
		handler: (editor) => {
			editor.ctx_current.globalCompositeOperation = "source-over"
			editor.ctx_current.rect(0, 0, editor.width, editor.height)
			editor.ctx_current.fill()
			editor.setBrush()
		},
		trackHistory: true
	},
	{
		id: "clear",
		name: "Clear",
		icon: "fa-solid fa-xmark",
		handler: (editor) => {
			editor.ctx_current.clearRect(0, 0, editor.width, editor.height)
		},
		trackHistory: true
	},
	{
		id: "undo",
		name: "Undo",
		icon: "fa-solid fa-rotate-left",
		handler: (editor) => {
			editor.history.undo()
		},
		trackHistory: false
	},
	{
		id: "redo",
		name: "Redo",
		icon: "fa-solid fa-rotate-right",
		handler: (editor) => {
			editor.history.redo()
		},
		trackHistory: false
	}
]

var IMAGE_EDITOR_SECTIONS = [
	{
		name: "tool",
		title: "Tool",
		default: "draw",
		options: Array.from(IMAGE_EDITOR_TOOLS.map(t => t.id)),
		initElement: (element, option) => {
			var tool_info = IMAGE_EDITOR_TOOLS.find(t => t.id == option)
			element.className = "image-editor-button button"
			var sub_element = document.createElement("div")
			var icon = document.createElement("i")
			tool_info.icon.split(" ").forEach(c => icon.classList.add(c))
			sub_element.appendChild(icon)
			sub_element.append(tool_info.name)
			element.appendChild(sub_element)
		}
	},
	{
		name: "color",
		title: "Color",
		default: "#f1c232",
		options: [
			"custom",
			"#ea9999", "#e06666", "#cc0000", "#990000", "#660000",
			"#f9cb9c", "#f6b26b", "#e69138", "#b45f06", "#783f04",
			"#ffe599", "#ffd966", "#f1c232", "#bf9000", "#7f6000",
			"#b6d7a8", "#93c47d", "#6aa84f", "#38761d", "#274e13",
			"#a4c2f4", "#6d9eeb", "#3c78d8", "#1155cc", "#1c4587",
			"#b4a7d6", "#8e7cc3", "#674ea7", "#351c75", "#20124d",
			"#d5a6bd", "#c27ba0", "#a64d79", "#741b47", "#4c1130",
			"#ffffff", "#c0c0c0", "#838383", "#525252", "#000000",
		],
		initElement: (element, option) => {
			if (option == "custom") {
				var input = document.createElement("input")
				input.type = "color"
				element.appendChild(input)
				var span = document.createElement("span")
				span.textContent = "Custom"
				span.onclick = function(e) {
					input.click()
				}
				element.appendChild(span)
			}
			else {
				element.style.background = option
			}
		},
		getCustom: editor => {
			var input = editor.popup.querySelector(".image_editor_color input")
			return input.value
		}
	},
	{
		name: "brush_size",
		title: "Brush Size",
		default: 48,
		options: [ 6, 12, 16, 24, 30, 40, 48, 64 ],
		initElement: (element, option) => {
			element.parentElement.style.flex = option
			element.style.width = option + "px"
			element.style.height = option + "px"
			element.style['margin-right'] = '2px'
			element.style["border-radius"] = (option / 2).toFixed() + "px"
		}
	},
	{
		name: "opacity",
		title: "Opacity",
		default: 0,
		options: [ 0, 0.2, 0.4, 0.6, 0.8 ],
		initElement: (element, option) => {
			element.style.background = `repeating-conic-gradient(rgba(0, 0, 0, ${option}) 0% 25%, rgba(255, 255, 255, ${option}) 0% 50%) 50% / 10px 10px`
		}
	},
	{
		name: "sharpness",
		title: "Sharpness",
		default: 0,
		options: [ 0, 0.05, 0.1, 0.2, 0.3 ],
		initElement: (element, option) => {
			var size = 32
			var blur_amount = parseInt(option * size)
			var sub_element = document.createElement("div")
			sub_element.style.background = `var(--background-color3)`
			sub_element.style.filter = `blur(${blur_amount}px)`
			sub_element.style.width = `${size - 2}px`
			sub_element.style.height = `${size - 2}px`
			sub_element.style['border-radius'] = `${size}px`
			element.style.background = "none"
			element.appendChild(sub_element)
		}
	}
]

class EditorHistory {
	constructor(editor) {
		this.editor = editor
		this.events = [] // stack of all events (actions/edits)
		this.current_edit = null
		this.rewind_index = 0 // how many events back into the history we've rewound to. (current state is just after event at index 'length - this.rewind_index - 1')
	}
	push(event) {
		// probably add something here eventually to save state every x events
		if (this.rewind_index != 0) {
			this.events = this.events.slice(0, 0 - this.rewind_index)
			this.rewind_index = 0
		}
		var snapshot_frequency = 20 // (every x edits, take a snapshot of the current drawing state, for faster rewinding)
		if (this.events.length > 0 && this.events.length % snapshot_frequency == 0) {
			event.snapshot = this.editor.layers.drawing.ctx.getImageData(0, 0, this.editor.width, this.editor.height)
		}
		this.events.push(event)
	}
	pushAction(action) {
		this.push({
			type: "action",
			id: action
		});
	}
	editBegin(x, y) {
		this.current_edit = {
			type: "edit",
			id: this.editor.getOptionValue("tool"),
			options: Object.assign({}, this.editor.options),
			points: [ { x: x, y: y } ]
		}
	}
	editMove(x, y) {
		if (this.current_edit) {
			this.current_edit.points.push({ x: x, y: y })
		}
	}
	editEnd(x, y) {
		if (this.current_edit) {
			this.push(this.current_edit)
			this.current_edit = null
		}
	}
	clear() {
		this.events = []
	}
	undo() {
		this.rewindTo(this.rewind_index + 1)
	}
	redo() {
		this.rewindTo(this.rewind_index - 1)
	}
	rewindTo(new_rewind_index) {
		if (new_rewind_index < 0 || new_rewind_index > this.events.length) {
			return; // do nothing if target index is out of bounds
		}

		var ctx = this.editor.layers.drawing.ctx
		ctx.clearRect(0, 0, this.editor.width, this.editor.height)

		var target_index = this.events.length - 1 - new_rewind_index
		var snapshot_index = target_index
		while (snapshot_index > -1) {
			if (this.events[snapshot_index].snapshot) {
				break
			}
			snapshot_index--
		}

		if (snapshot_index != -1) {
			ctx.putImageData(this.events[snapshot_index].snapshot, 0, 0);
		}

		for (var i = (snapshot_index + 1); i <= target_index; i++) {
			var event = this.events[i]
			if (event.type == "action") {
				var action = IMAGE_EDITOR_ACTIONS.find(a => a.id == event.id)
				action.handler(this.editor)
			}
			else if (event.type == "edit") {
				var tool = IMAGE_EDITOR_TOOLS.find(t => t.id == event.id)
				this.editor.setBrush(this.editor.layers.drawing, event.options)

				var first_point = event.points[0]
				tool.begin(this.editor, ctx, first_point.x, first_point.y)
				for (var point_i = 1; point_i < event.points.length; point_i++) {
					tool.move(this.editor, ctx, event.points[point_i].x, event.points[point_i].y)
				}
				var last_point = event.points[event.points.length - 1]
				tool.end(this.editor, ctx, last_point.x, last_point.y)
			}
		}

		// re-set brush to current settings
		this.editor.setBrush(this.editor.layers.drawing)

		this.rewind_index = new_rewind_index
	}
}

class ImageEditor {
	constructor(popup, inpainter = false) {
		this.inpainter = inpainter
		this.popup = popup
		this.history = new EditorHistory(this)
		if (inpainter) {
			this.popup.classList.add("inpainter")
		}
		this.drawing = false
		this.temp_previous_tool = null // used for the ctrl-colorpicker functionality
		this.container = popup.querySelector(".editor-controls-center > div")
		this.layers = {}
		var layer_names = [
			"background",
			"drawing",
			"overlay"
		]
		layer_names.forEach(name => {
			let canvas = document.createElement("canvas")
			canvas.className = `editor-canvas-${name}`
			this.container.appendChild(canvas)
			this.layers[name] = {
				name: name,
				canvas: canvas,
				ctx: canvas.getContext("2d")
			}
		})

		// add mouse handlers
		this.container.addEventListener("mousedown", this.mouseHandler.bind(this))
		this.container.addEventListener("mouseup", this.mouseHandler.bind(this))
		this.container.addEventListener("mousemove", this.mouseHandler.bind(this))
		this.container.addEventListener("mouseout", this.mouseHandler.bind(this))
		this.container.addEventListener("mouseenter", this.mouseHandler.bind(this))

		this.container.addEventListener("touchstart", this.mouseHandler.bind(this))
		this.container.addEventListener("touchmove", this.mouseHandler.bind(this))
		this.container.addEventListener("touchcancel", this.mouseHandler.bind(this))
		this.container.addEventListener("touchend", this.mouseHandler.bind(this))

		// initialize editor controls
		this.options = {}
		this.optionElements = {}
		IMAGE_EDITOR_SECTIONS.forEach(section => {
			section.id = `image_editor_${section.name}`
			var sectionElement = document.createElement("div")
			sectionElement.className = section.id
	
			var title = document.createElement("h4")
			title.innerText = section.title
			sectionElement.appendChild(title)
	
			var optionsContainer = document.createElement("div")
			optionsContainer.classList.add("editor-options-container")
	
			this.optionElements[section.name] = []
			section.options.forEach((option, index) => {
				var optionHolder = document.createElement("div")
				var optionElement = document.createElement("div")
				optionHolder.appendChild(optionElement)
				section.initElement(optionElement, option)
				optionElement.addEventListener("click", target => this.selectOption(section.name, index))
				optionsContainer.appendChild(optionHolder)
				this.optionElements[section.name].push(optionElement)
			})
			this.selectOption(section.name, section.options.indexOf(section.default))
	
			sectionElement.appendChild(optionsContainer)
	
			this.popup.querySelector(".editor-controls-left").appendChild(sectionElement)
		})

		this.custom_color_input = this.popup.querySelector(`input[type="color"]`)
		this.custom_color_input.addEventListener("change", () => {
			this.custom_color_input.parentElement.style.background = this.custom_color_input.value
			this.selectOption("color", 0)
		})

		if (this.inpainter) {
			this.selectOption("color", IMAGE_EDITOR_SECTIONS.find(s => s.name == "color").options.indexOf("#ffffff"))
			this.selectOption("opacity", IMAGE_EDITOR_SECTIONS.find(s => s.name == "opacity").options.indexOf(0.4))
		}

		// initialize the right-side controls
		var buttonContainer = document.createElement("div")
		IMAGE_EDITOR_BUTTONS.forEach(button => {
			var element = document.createElement("div")
			var icon = document.createElement("i")
			element.className = "image-editor-button button"
			icon.className = button.icon
			element.appendChild(icon)
			element.append(button.name)
			buttonContainer.appendChild(element)
			element.addEventListener("click", event => button.handler(this))
		})
		var actionsContainer = document.createElement("div")
		var actionsTitle = document.createElement("h4")
		actionsTitle.textContent = "Actions"
		actionsContainer.appendChild(actionsTitle);
		IMAGE_EDITOR_ACTIONS.forEach(action => {
			var element = document.createElement("div")
			var icon = document.createElement("i")
			element.className = "image-editor-button button"
			if (action.className) {
				element.className += " " + action.className
			}
			icon.className = action.icon
			element.appendChild(icon)
			element.append(action.name)
			actionsContainer.appendChild(element)
			element.addEventListener("click", event => this.runAction(action.id))
		})
		this.popup.querySelector(".editor-controls-right").appendChild(actionsContainer)
		this.popup.querySelector(".editor-controls-right").appendChild(buttonContainer)

		this.keyHandlerBound = this.keyHandler.bind(this)

		this.setSize(512, 512)
	}
	show() {
		this.popup.classList.add("active")
		document.addEventListener("keydown", this.keyHandlerBound)
		document.addEventListener("keyup", this.keyHandlerBound)
	}
	hide() {
		this.popup.classList.remove("active")
		document.removeEventListener("keydown", this.keyHandlerBound)
		document.removeEventListener("keyup", this.keyHandlerBound)
	}
	setSize(width, height) {
		if (width == this.width && height == this.height) {
			return
		}

		if (width > height) {
		        var max_size = Math.min(parseInt(window.innerWidth * 0.9), width, 768)
			var multiplier = max_size / width
			width = (multiplier * width).toFixed()
			height = (multiplier * height).toFixed()
		}
		else {
		        var max_size = Math.min(parseInt(window.innerHeight * 0.9), height, 768)
			var multiplier = max_size / height
			width = (multiplier * width).toFixed()
			height = (multiplier * height).toFixed()
		}
		this.width = parseInt(width)
		this.height = parseInt(height)
	
		this.container.style.width = width + "px"
		this.container.style.height = height + "px"
		
		Object.values(this.layers).forEach(layer => {
			layer.canvas.width = width
			layer.canvas.height = height
		})

		if (this.inpainter) {
			this.saveImage() // We've reset the size of the image so inpainting is different
		}
		this.setBrush()
		this.history.clear()
	}
	get tool() {
		var tool_id = this.getOptionValue("tool")
		return IMAGE_EDITOR_TOOLS.find(t => t.id == tool_id);
	}
	loadTool() {
		this.drawing = false
		this.container.style.cursor = this.tool.cursor;
	}
	setImage(url, width, height) {
		this.setSize(width, height)
		this.layers.background.ctx.clearRect(0, 0, this.width, this.height)
		if (!(url && this.inpainter)) {
			this.layers.drawing.ctx.clearRect(0, 0, this.width, this.height)
		}
		if (url) {
			var image = new Image()
			image.onload = () => { 
				this.layers.background.ctx.drawImage(image, 0, 0, this.width, this.height)
			}
			image.src = url
		}
		else {
			this.layers.background.ctx.fillStyle = "#ffffff"
			this.layers.background.ctx.beginPath()
			this.layers.background.ctx.rect(0, 0, this.width, this.height)
			this.layers.background.ctx.fill()
		}
		this.history.clear()
	}
	saveImage() {
		if (!this.inpainter) {
			// This is not an inpainter, so save the image as the new img2img input
			this.layers.background.ctx.drawImage(this.layers.drawing.canvas, 0, 0, this.width, this.height)
			var base64 = this.layers.background.canvas.toDataURL()
			initImagePreview.src = base64 // this will trigger the rest of the app to use it
		}
		else {
			// This is an inpainter, so make sure the toggle is set accordingly
			var is_blank = !this.layers.drawing.ctx
				.getImageData(0, 0, this.width, this.height).data
				.some(channel => channel !== 0)
			maskSetting.checked = !is_blank
		}
		this.hide()
	}
	getImg() { // a drop-in replacement of the drawingboard version
		return this.layers.drawing.canvas.toDataURL()
	}
	setImg(dataUrl) { // a drop-in replacement of the drawingboard version
		var image = new Image()
		image.onload = () => {
			var ctx = this.layers.drawing.ctx;
			ctx.clearRect(0, 0, this.width, this.height)
			ctx.globalCompositeOperation = "source-over"
			ctx.globalAlpha = 1
			ctx.filter = "none"
			ctx.drawImage(image, 0, 0, this.width, this.height)
			this.setBrush(this.layers.drawing)
		}
		image.src = dataUrl
	}
	runAction(action_id) {
		var action = IMAGE_EDITOR_ACTIONS.find(a => a.id == action_id)
		if (action.trackHistory) {
			this.history.pushAction(action_id)
		}
		action.handler(this)
	}
	setBrush(layer = null, options = null) {
		if (options == null) {
			options = this.options
		}
		if (layer) {
			layer.ctx.lineCap = "round"
			layer.ctx.lineJoin = "round"
			layer.ctx.lineWidth = options.brush_size
			layer.ctx.fillStyle = options.color
			layer.ctx.strokeStyle = options.color
			var sharpness = parseInt(options.sharpness * options.brush_size)
			layer.ctx.filter = sharpness == 0 ? `none` : `blur(${sharpness}px)`
			layer.ctx.globalAlpha = (1 - options.opacity)
			layer.ctx.globalCompositeOperation = "source-over"
			var tool = IMAGE_EDITOR_TOOLS.find(t => t.id == options.tool)
			if (tool && tool.setBrush) {
				tool.setBrush(editor, layer)
			}
		}
		else {
			Object.values([ "drawing", "overlay" ]).map(name => this.layers[name]).forEach(l => {
				this.setBrush(l)
			})
		}
	}
	get ctx_overlay() {
		return this.layers.overlay.ctx
	}
	get ctx_current() { // the idea is this will help support having custom layers and editing each one
		return this.layers.drawing.ctx
	}
	get canvas_current() {
		return this.layers.drawing.canvas
	}
	keyHandler(event) { // handles keybinds like ctrl+z, ctrl+y
		if (!this.popup.classList.contains("active")) {
			document.removeEventListener("keydown", this.keyHandlerBound)
			document.removeEventListener("keyup", this.keyHandlerBound)
			return // this catches if something else closes the window but doesnt properly unbind the key handler
		}

		// keybindings
		if (event.type == "keydown") {
			if ((event.key == "z" || event.key == "Z") && event.ctrlKey) {
				if (!event.shiftKey) {
					this.history.undo()
				}
				else {
					this.history.redo()
				}
			}
			if (event.key == "y" && event.ctrlKey) {
				this.history.redo()
			}
			if (event.key === "Escape") {
				this.hide()
			}
		}
		
		// dropper ctrl holding handler stuff
		var dropper_active = this.temp_previous_tool != null;
		if (dropper_active && !event.ctrlKey) {
			this.selectOption("tool", IMAGE_EDITOR_TOOLS.findIndex(t => t.id == this.temp_previous_tool))
			this.temp_previous_tool = null
		}
		else if (!dropper_active && event.ctrlKey) {
			this.temp_previous_tool = this.getOptionValue("tool")
			this.selectOption("tool", IMAGE_EDITOR_TOOLS.findIndex(t => t.id == "colorpicker"))
		}
	}
	mouseHandler(event) {
		var bbox = this.layers.overlay.canvas.getBoundingClientRect()
		var x = (event.clientX || 0) - bbox.left
		var y = (event.clientY || 0) - bbox.top
		var type = event.type;
		var touchmap = {
			touchstart: "mousedown",
			touchmove: "mousemove",
			touchend: "mouseup",
			touchcancel: "mouseup"
		}
		if (type in touchmap) {
			type = touchmap[type]
			if (event.touches && event.touches[0]) {
				var touch = event.touches[0]				
				var x = (touch.clientX || 0) - bbox.left
				var y = (touch.clientY || 0) - bbox.top
			}
		}
		event.preventDefault()	
		// do drawing-related stuff
		if (type == "mousedown" || (type == "mouseenter" && event.buttons == 1)) {
			this.drawing = true
			this.tool.begin(this, this.ctx_current, x, y)
			this.tool.begin(this, this.ctx_overlay, x, y, true)
			this.history.editBegin(x, y)
		}
		if (type == "mouseup" || type == "mousemove") {
			if (this.drawing) {
				if (x > 0 && y > 0) {
					this.tool.move(this, this.ctx_current, x, y)
					this.tool.move(this, this.ctx_overlay, x, y, true)
					this.history.editMove(x, y)
				}
			}
		}
		if (type == "mouseup" || type == "mouseout") {
			if (this.drawing) {
				this.drawing = false
				this.tool.end(this, this.ctx_current, x, y)
				this.tool.end(this, this.ctx_overlay, x, y, true)
				this.history.editEnd(x, y)
			}
		}
	}
	getOptionValue(section_name) {
		var section = IMAGE_EDITOR_SECTIONS.find(s => s.name == section_name)
		return this.options && section_name in this.options ? this.options[section_name] : section.default
	}
	selectOption(section_name, option_index) {
		var section = IMAGE_EDITOR_SECTIONS.find(s => s.name == section_name)
		var value = section.options[option_index]
		this.options[section_name] = value == "custom" ? section.getCustom(this) : value
		
		this.optionElements[section_name].forEach(element => element.classList.remove("active"))
		this.optionElements[section_name][option_index].classList.add("active")
	
		// change the editor
		this.setBrush()
		if (section.name == "tool") {
			this.loadTool()
		}
	}
}

const imageEditor = new ImageEditor(document.getElementById("image-editor"))
const imageInpainter = new ImageEditor(document.getElementById("image-inpainter"), true)

imageEditor.setImage(null, 512, 512)
imageInpainter.setImage(null, 512, 512)

document.getElementById("init_image_button_draw").addEventListener("click", () => {
	imageEditor.show()
})
document.getElementById("init_image_button_inpaint").addEventListener("click", () => {
	imageInpainter.show()
})

img2imgUnload() // no init image when the app starts


function rgbToHex(rgb) {
	function componentToHex(c) {
		var hex = parseInt(c).toString(16)
		return hex.length == 1 ? "0" + hex : hex
	}
	return "#" + componentToHex(rgb.r) + componentToHex(rgb.g) + componentToHex(rgb.b)
}

function hexToRgb(hex) {
	var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
	return result ? {
		r: parseInt(result[1], 16),
		g: parseInt(result[2], 16),
		b: parseInt(result[3], 16)
	} : null;
}

function pixelCompare(int1, int2) {
	return Math.abs(int1 - int2) < 4
}

// adapted from https://ben.akrin.com/canvas_fill/fill_04.html
function flood_fill(editor, the_canvas_context, x, y, color) {
	pixel_stack = [{x:x, y:y}] ;
	pixels = the_canvas_context.getImageData( 0, 0, editor.width, editor.height ) ;
	var linear_cords = ( y * editor.width + x ) * 4 ;
	var original_color = {r:pixels.data[linear_cords],
						g:pixels.data[linear_cords+1],
						b:pixels.data[linear_cords+2],
						a:pixels.data[linear_cords+3]} ;
	
	var opacity = color.a / 255;
	var new_color = {
		r: parseInt((color.r * opacity) + (original_color.r * (1 - opacity))),
		g: parseInt((color.g * opacity) + (original_color.g * (1 - opacity))),
		b: parseInt((color.b * opacity) + (original_color.b * (1 - opacity)))
	}

	if ((pixelCompare(new_color.r, original_color.r) &&
		pixelCompare(new_color.g, original_color.g) &&
		pixelCompare(new_color.b, original_color.b)))
	{
		return; // This color is already the color we want, so do nothing
	}
	var max_stack_size = editor.width * editor.height;
	while( pixel_stack.length > 0 && pixel_stack.length < max_stack_size ) {
		new_pixel = pixel_stack.shift() ;
		x = new_pixel.x ;
		y = new_pixel.y ;
	
		linear_cords = ( y * editor.width + x ) * 4 ;
		while( y-->=0 &&
			   (pixelCompare(pixels.data[linear_cords], original_color.r) &&
				pixelCompare(pixels.data[linear_cords+1], original_color.g) &&
				pixelCompare(pixels.data[linear_cords+2], original_color.b))) {
			linear_cords -= editor.width * 4 ;
		}
		linear_cords += editor.width * 4 ;
		y++ ;

		var reached_left = false ;
		var reached_right = false ;
		while( y++<editor.height &&
			   (pixelCompare(pixels.data[linear_cords], original_color.r) &&
				pixelCompare(pixels.data[linear_cords+1], original_color.g) &&
				pixelCompare(pixels.data[linear_cords+2], original_color.b))) {
			pixels.data[linear_cords]   = new_color.r ;
			pixels.data[linear_cords+1] = new_color.g ;
			pixels.data[linear_cords+2] = new_color.b ;
			pixels.data[linear_cords+3] = 255 ;

			if( x>0 ) {
				if( pixelCompare(pixels.data[linear_cords-4], original_color.r) &&
					pixelCompare(pixels.data[linear_cords-4+1], original_color.g) &&
					pixelCompare(pixels.data[linear_cords-4+2], original_color.b)) {
					if( !reached_left ) {
						pixel_stack.push( {x:x-1, y:y} ) ;
						reached_left = true ;
					}
				} else if( reached_left ) {
					reached_left = false ;
				}
			}
		
			if( x<editor.width-1 ) {
				if( pixelCompare(pixels.data[linear_cords+4], original_color.r) &&
					pixelCompare(pixels.data[linear_cords+4+1], original_color.g) &&
					pixelCompare(pixels.data[linear_cords+4+2], original_color.b)) {
					if( !reached_right ) {
						pixel_stack.push( {x:x+1,y:y} ) ;
						reached_right = true ;
					}
				} else if( reached_right ) {
					reached_right = false ;
				}
			}
			
			linear_cords += editor.width * 4 ;
		}
	}
	the_canvas_context.putImageData( pixels, 0, 0 ) ;
}