# Hurry Curry! - a game about cooking
# Copyright (C) 2026 Hurry Curry! Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
class_name Diagram
extends Control

const RENDERER := preload("res://gui/components/message/renderer.tscn")
var BASE_SIZE: float = 64.;
var hsize: float = 32.;

var raw: Dictionary
var nodes: Array#[DiagramNode]
var edges: Array#[DiagramEdge]
var draw_nodes: Array
var renderers: Array[Renderer] = []

class DiagramNode:
	var position: Vector2
	var label: MessageParser
	var style: String

	func _init(raw: Dictionary, game: Game):
		position = Vector2(raw["position"][0], raw["position"][1])
		label = MessageParser.new(raw["label"], game)
		style = raw["style"]

class DiagramEdge:
	var src: int
	var dst: int

	func _init(raw: Dictionary):
		src = raw["src"]
		dst = raw["dst"]

func _init(raw_: Dictionary, game: Game) -> void:
	raw = raw_
	nodes = raw["nodes"].map(func(r): return DiagramNode.new(r, game))
	edges = raw["edges"].map(func(r): return DiagramEdge.new(r))
	# ugh
	draw_nodes = raw["nodes"].map(func(r): return DiagramNode.new(r, game))

func _ready() -> void:
	redraw_images()
	item_rect_changed.connect(func():
		if size != Vector2(64, 64): scale()
	)

func redraw_images() -> void:
	for r in renderers:
		r.queue_free()
	renderers.clear()
	for n: DiagramNode in draw_nodes:
		var r: Renderer = RENDERER.instantiate()
		r.get_node("SubViewport").size = Vector2.ONE * hsize * 2.
		add_child(r)
		r.position = n.position - Vector2(hsize, hsize)
		match n.label.kind:
			MessageParser.Kind.ITEM: r.setup_object(n.label.result)
			MessageParser.Kind.TILE: r.setup_object([n.label.result])
		renderers.push_back(r)

func scale() -> void:
	var bounds = Rect2(Vector2.ONE * BASE_SIZE * 0.5, Vector2(size.x - BASE_SIZE, size.y - BASE_SIZE))
	var current := Rect2(Vector2(INF, INF), Vector2(-INF, -INF))
	for n: DiagramNode in nodes:
		current.position = current.position.min(n.position)
		current.end = current.end.max(n.position)
	var s = Vector2.ONE.min(bounds.size / current.size)
	hsize = BASE_SIZE * 0.5 * min(s.x, s.y)
	for i in nodes.size():
		var dn: DiagramNode = draw_nodes[i]
		var n: DiagramNode = nodes[i]
		dn.position = n.position - current.position
		dn.position *= s
		dn.position += bounds.position
	redraw_images()

func _draw() -> void:
	for n: DiagramNode in draw_nodes:
		match n.style:
			"intermediate_product":
				draw_circle(n.position, hsize, Color("#555"), true, -1., true)
			"final_product":
				draw_circle(n.position, hsize, Color("#333"), true, -1., true)
			"process_active":
				draw_rect(Rect2(n.position - Vector2(hsize, hsize), Vector2(hsize, hsize) * 2.), Color("#47c42b"))
			"process_passive":
				draw_rect(Rect2(n.position - Vector2(hsize, hsize), Vector2(hsize, hsize) * 2.), Color("#c4a32b"))
			"process_instant":
				draw_rect(Rect2(n.position - Vector2(hsize, hsize), Vector2(hsize, hsize) * 2.), Color("#5452d8"))

	for e: DiagramEdge in edges:
		var src_node: DiagramNode = draw_nodes[e.src]
		var dst_node: DiagramNode = draw_nodes[e.dst]
		var src := node_edge_connect_pos(src_node, dst_node)
		var dst := node_edge_connect_pos(dst_node, src_node)

		var dir = (src - dst).normalized()
		var tip0 = dst
		var tip1 = dst + dir.rotated(0.2 * PI) * 10.
		var tip2 = dst + dir.rotated(-0.2 * PI) * 10.
		dst += dir * 5.

		draw_colored_polygon([tip0, tip1, tip2], Color.BLACK)
		draw_line(src, dst, Color.BLACK, 2., true)

func node_edge_connect_pos(src: DiagramNode, dst: DiagramNode) -> Vector2:
	var dir = (dst.position - src.position).normalized()
	if src.style == "intermediate_product" or src.style == "final_product":
		return src.position + dir * hsize
	else:
		return src.position + dir / max(abs(dir.y), abs(dir.x)) * hsize
