# Hurry Curry! - a game about cooking
# Copyright (C) 2025 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")
const SIZE: float = 64.;
const HSIZE: float = SIZE / 2.;

var raw: Dictionary
var nodes: Array#[DiagramNode]
var edges: Array#[DiagramEdge]

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"])
		style = raw["style"]
		label.parse(game)

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))

func _ready() -> void:
	for n: DiagramNode in nodes:
		var r: Renderer = RENDERER.instantiate()
		r.get_node("SubViewport").size = Vector2i(SIZE, SIZE)
		add_child(r)
		r.position = n.position - Vector2(HSIZE, HSIZE)
		match n.label.kind:
			MessageParser.Kind.ITEM: r.mode = Renderer.Mode.ITEMS
			MessageParser.Kind.TILE: r.mode = Renderer.Mode.TILES
		r.setup_object(n.label.result)

func scale(bounds: Rect2):
	var current := Rect2(Vector2(INF, INF), Vector2(-INF, -INF))
	for n: DiagramNode in nodes:
		if n.position.x < current.position.x:
			current.position.x = n.position.x
		if n.position.y < current.position.y:
			current.position.y = n.position.y
		if n.position.x > current.end.x:
			current.end.x = n.position.x
		if n.position.y > current.end.y:
			current.end.y = n.position.y

	var s := bounds.size / current.size
	for n: DiagramNode in nodes:
		var p := n.position
		p -= current.position
		p *= s
		n.position = p + bounds.position

func _draw() -> void:
	for n: DiagramNode in 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(SIZE, SIZE)), Color("#47c42b"))
			"process_passive":
				draw_rect(Rect2(n.position - Vector2(HSIZE, HSIZE), Vector2(SIZE, SIZE)), Color("#c4a32b"))
			"process_instant":
				draw_rect(Rect2(n.position - Vector2(HSIZE, HSIZE), Vector2(SIZE, SIZE)), Color("#5452d8"))

	for e: DiagramEdge in edges:
		var src_node: DiagramNode = nodes[e.src]
		var dst_node: DiagramNode = 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 / abs(dir.y) * HSIZE
