# SPDX-FileCopyrightText: (C) 2022 Frederic Croix <thefgfseagle@gmail.com>
# SPDX-License-Identifier: GPL-2.0-or-later

gui.Popup = {
	__used_ids: [],
	# Constructor
	#
	# @param size ([width, height])
	new: func(size_, id = nil, parent = nil) {
		if (id == nil or contains(gui.Popup.__used_ids, id)) {
			id = "popup" ~ size(gui.Popup.__used_ids);
		}
		append(gui.Popup.__used_ids, id);
		var ghost = _newWindowGhost(id);
		var m = {
			parents: [gui.Popup, PropertyElement, ghost],
			_ghost: ghost,
			_node: props.wrapNode(ghost._node_ghost),
			_focused: 0,
			_widgets: [],
			_parent: parent,
			_canvas: nil,
		};

		m.setInt("content-size[0]", size_[0]);
		m.setInt("content-size[1]", size_[1]);
		
		m.setFocus();

		# arg = [child, listener_node, mode, is_child_event]
		setlistener(m._node, func m._propCallback(arg[0], arg[2]), 0, 2);
		
		return m;
	},
	# Destructor
	del: func {
		me.clearFocus();
		
		if (me["_canvas"] != nil) {
			var placements = me._canvas._node.getChildren("placement");
			# Do not remove canvas if other placements exist
			if (size(placements) > 1) {
				foreach (var p; placements) {
					if (p.getValue("type") == "window" and p.getValue("id") == me.get("id")) {
						p.remove();
					}
				}
			} else {
				me._canvas.del();
			}
			me._canvas = nil;
		}
		if (me._node != nil) {
			me._node.remove();
			me._node = nil;
		}
	},
	# Create the canvas to be used for this Window
	#
	# @return The new canvas
	createCanvas: func() {
		var size = [
			me.get("content-size[0]"),
			me.get("content-size[1]")
		];
		
		me._canvas = new({
			size: [size[0], size[1]],
			view: size,
			placement: {
				type: "window",
				id: me.get("id")
			},
			
			# Standard alpha blending
			"blend-source-rgb": "src-alpha",
			"blend-destination-rgb": "one-minus-src-alpha",
			
			# Just keep current alpha (TODO allow using rgb textures instead of rgba?)
			"blend-source-alpha": "zero",
			"blend-destination-alpha": "one"
		});
		
		me._canvas._focused_widget = nil;
		me._canvas.data("focused", me._focused);
		
		return me._canvas;
	},
	# Set an existing canvas to be used for this Window
	setCanvas: func(canvas_) {
		if (ghosttype(canvas_) != "Canvas") {
			return debug.warn("Not a Canvas");
		}
		
		canvas_.addPlacement({type: "window", "id": me.get("id")});
		me['_canvas'] = canvas_;
		
		canvas_._focused_widget = nil;
		canvas_.data("focused", me._focused);
		
		return me;
	},
	# Get the displayed canvas
	getCanvas: func(create = 0) {
		if (me['_canvas'] == nil and create) {
			me.createCanvas();
		}
		
		return me['_canvas'];
	},
	setLayout: func(l) {
		if (me['_canvas'] == nil) {
			me.createCanvas();
		}
		
		me._canvas.update(); # Ensure placement is applied
		me._ghost.setLayout(l);
		return me;
	},
	#
	setFocus: func {
		if (gui.focused_window != nil) {
			gui.focused_window.clearFocus();
		}
		
#		me.onFocusIn();
		me._focused = 1;
		me._onStateChange();
		gui.focused_window = me;
		setInputFocus(me);
		return me;
	},
	#
	clearFocus: func {
#		me.onFocusOut();
		me._focused = 0;
		me._onStateChange();
		if (gui.focused_window == me) {
			gui.focused_window = nil;
			setInputFocus(nil);
		}
		if (me._parent != nil and contains(gui.open_popups, me._parent)) {
			me._parent.setFocus();
		}
		return me;
	},
	setPosition: func {
		if (size(arg) == 1) {
			var arg = arg[0];
		}
		var (x, y) = arg;
		
		me.setInt("tf/t[0]", x);
		me.setInt("tf/t[1]", y);
		return me;
	},
	setSize: func {
		if (size(arg) == 1) {
			var arg = arg[0];
		}
		var (w, h) = arg;
		
		me.set("content-size[0]", w);
		me.set("content-size[1]", h);
		
		if (me.onResize != nil) {
			me.onResize();
		}
		
		return me;
	},
	getSize: func {
		var w = me.get("content-size[0]");
		var h = me.get("content-size[1]");
		return [w,h];
	},
	# Raise to top of window stack
	raise: func() {
		# on writing the z-index the window always is moved to the top of all other
		# windows with the same z-index.
		me.setInt("z-index", me.get("z-index", gui.STACK_INDEX["always-on-top"]));
		
		me.setFocus();
	},
	hide: func(parents = 0) {
		me.clearFocus();
		me._ghost.hide();
		for (var i = 0; i < size(gui.open_popups); i += 1) {
			if (gui.open_popups[i] == me) {
				gui.open_popups = subvec(gui.open_popups, 0, i) ~ subvec(gui.open_popups, i);
				break;
			}
		}
		if (me._parent != nil) {
			me._parent.setFocus();
		}
	},
	show: func() {
		me._ghost.show();
		me.raise();
		if (me._canvas != nil) {
			me._canvas.update();
		}
		for (var i = 0; i < size(gui.open_popups); i += 1) {
			gui.open_popups[i].clearFocus();
		}
		append(gui.open_popups, me);
	},
	# Hide / show the window based on whether it's currently visible
	toggle: func() {
		if (me.isVisible()) {
			me.hide();
		} else {
			me.show();
			me.raise();
		}
	},
	onResize: func() {
		if (me['_canvas'] == nil) {
			return;
		}
		
		for(var i = 0; i < 2; i += 1) {
			var size = me.get("content-size[" ~ i ~ "]");
			me._canvas.set("size[" ~ i ~ "]", size);
			me._canvas.set("view[" ~ i ~ "]", size);
		}
	},
# protected:
	_onStateChange: func {
		var event = canvas.CustomEvent.new("wm.focus-" ~ (me._focused ? "in" : "out"));
		
		if (me.getCanvas() != nil) {
			me.getCanvas().data("focused", me._focused).dispatchEvent(event);
		}
	},
# private:
	#mode 0 = value changed, +-1 add/remove node
	_propCallback: func(child, mode) {
		if (!me._node.equals(child.getParent())) {
			return;
		}
		var name = child.getName();
		
		# support for CSS like position: absolute; with right and/or bottom margin
		if (name == "right") {
			me._handlePositionAbsolute(child, mode, name, 0);
		} elsif (name == "bottom") {
			me._handlePositionAbsolute(child, mode, name, 1);
		}
	},
	_handlePositionAbsolute: func(child, mode, name, index) {
		# mode
		#	 -1 child removed
		#		0 value changed
		#		1 child added

		if (mode == 0) {
			me._updatePos(index, name);
		} elsif (mode == 1) {
			me["_listener_" ~ name] = [
				setlistener(
					"/sim/gui/canvas/size[" ~ index ~ "]",
					func me._updatePos(index, name)
				),
				setlistener(
					me._node.getNode("content-size[" ~ index ~ "]"),
					func me._updatePos(index, name)
				)
			];
		} elsif (mode == -1) {
			for (var i = 0; i < 2; i += 1) {
				removelistener(me["_listener_" ~ name][i]);
			}
		}
	},
	_updatePos: func(index, name) {
		me.setInt(
			"tf/t[" ~ index ~ "]",
			getprop("/sim/gui/canvas/size[" ~ index ~ "]") - me.get(name) - me.get("content-size[" ~ index ~ "]")
		);
	},
};