# 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 Map
extends Node3D

class TileInfo:
	func _init(position_, name_, tile_, neighbours_) -> void:
		position = position_; name = name_; tile = tile_; neighbours = neighbours_
	var position: Vector2i
	var name: String
	var tile: Tile
	var neighbours: Array

var tile_by_pos: Dictionary[Vector2i, TileInfo] = {}
var autoflush = false
var currently_baked = false
var floor_node := MeshInstance3D.new()
var tile_factory = TileFactory.new()

func get_tile_name(pos: Vector2i): # -> String?
	var e = tile_by_pos.get(pos)
	if e != null: return e.name
	else: return null
func get_tile_instance(pos: Vector2i) -> Tile:
	var e = tile_by_pos.get(pos)
	if e != null: return e.tile
	else: return null

func set_tile(pos: Vector2i, tilename = null, neighbors: Array = [null,null,null,null]):
	_remove_tile(pos)
	if tilename != null:
		_add_tile(pos, tilename, neighbors)
	if autoflush: flush()

func _add_tile(pos: Vector2i, tilename: String, neighbors: Array) -> Tile:
	var tile := tile_factory.produce(tilename, pos, neighbors)
	add_child(tile)
	tile.position = Vector3(pos.x, 0, pos.y)
	tile_by_pos[pos] = TileInfo.new(pos, tilename, tile, neighbors)
	return tile

func _remove_tile(pos: Vector2i):
	var tile = get_tile_instance(pos)
	if tile == null: return
	if tile.item != null: tile.item.queue_free()
	if tile is FloorLike:
		var floor_mesher = tile_factory.floor_meshers.get(tile.fm_id())
		if floor_mesher != null:
			floor_mesher.remove_tile(pos)
	tile.queue_free()
	tile_by_pos.erase(pos)
	tile.name += "_queued_free"

@onready var voxelgi: VoxelGI = $VoxelGI

func _ready():
	Settings.hook_changed("graphics.gi", self, apply_gi_setting)
	floor_node.material_override = preload("res://map/tiles/floor_material.tres")
	for fm in tile_factory.floor_meshers.values():
		add_child(fm.mesh_instance)
	add_child(floor_node)

func flush() -> void:
	for fm in tile_factory.floor_meshers.values():
		fm.flush()
	gi_bake()

func apply_gi_setting(state):
	if state == "voxelgi" and not currently_baked:
		gi_bake()
	else:
		currently_baked = false
		voxelgi.data = null

func gi_bake():
	if Settings.read("graphics.gi") != "voxelgi": return
	print("Map: Rebaking VoxelGI")
	currently_baked = true
	gi_bake_blocking()

func gi_bake_blocking():
	var map_extents = extents()
	var extent_min = map_extents[0]
	var extent_max = map_extents[1]
	var center = (extent_max + extent_min) / 2
	var size = extent_max - extent_min
	voxelgi.position = Vector3(center.x, 3., center.y)
	voxelgi.size = Vector3(size.x, 8., size.y)
	print("Baking now!")
	var start = Time.get_ticks_msec()
	voxelgi.bake()
	voxelgi.visible = true
	print("Bake done. elapsed=", Time.get_ticks_msec() - start)

func extents() -> Array[Vector2]:
	var extent_min = Vector2(0,0)
	var extent_max = Vector2(0,0)
	for e: TileInfo in tile_by_pos.values():
		extent_min.x = min(extent_min.x, e.position.x)
		extent_min.y = min(extent_min.y, e.position.y)
		extent_max.x = max(extent_max.x, e.position.x)
		extent_max.y = max(extent_max.y, e.position.y)
	return [extent_min, extent_max + Vector2(1., 1.)]
