class_name Adventurer extends Node


enum Gender{
	MASC,
	FEMME,
	NONBINARY
}

var given_name : String = "Test"
var surname : String = "Testing"
var gender : Gender = Gender.MASC
var life : int = 1
var max_life : int = 1
var energy : int = 1
var max_energy : int = 1
var level : int = 1
var exp : int = 0
var job : JobData
var appearance : Dictionary
var stats : StatBlock
var gold : int = 0
var quest : Quest
var equipment : Array = [null, null, null]
var inventory : Dictionary[Vector2, Item] = {}
var inventory_size : Vector2i = Vector2i(3,2)
var inventory_count : int = 0

var quest_sprite : QuestSprite

signal changed()
signal levelled()
signal died()

func _init() -> void:
	stats = StatBlock.new()

func generate() -> void:
	if job == null:
		return
	stats.STR = randi_range(job.min_STR, job.max_STR)
	stats.DEX = randi_range(job.min_DEX, job.max_DEX)
	stats.INT = randi_range(job.min_INT, job.max_INT)
	stats.CHA = randi_range(job.min_CHA, job.max_CHA)
	stats.FAI = randi_range(job.min_FAI, job.max_FAI)
	stats.LUK = randi_range(job.min_LUK, job.max_LUK)
	generate_appearance()
	max_life = stats.STR * 10 + stats.CHA * 10
	max_energy = stats.INT * 10 + stats.FAI * 10
	life = max_life
	energy = max_energy
	changed.emit()

func assign_quest(quest : Quest) -> void:
	self.quest = quest
	quest.initiate(self)
	changed.emit()

func full_name() -> String:
	return given_name + " " + surname

func gain_level() -> void:
	level += 1
	#TODO: Make stats improve based on job
	Game.notice("%s has reached level %d!" % [full_name(), level])
	changed.emit()
	levelled.emit()

func gain_exp(amount : int) -> void:
	exp += amount
	while exp >= get_tnl():
		exp -= get_tnl()
		gain_level()
	changed.emit()

func gain_gold(amount :int) -> void:
	gold += amount
	changed.emit()

func get_tnl() -> int:
	if job:
		return job.get_tnl(level)
	else:
		return level * 10

func find_open_inventory_slot(item : Item) -> Vector2:
	var first_open : Vector2 = Vector2(-1,-1)
	for j in range(inventory_size.y):
		for i in range(inventory_size.x):
			var curr_slot : Item = inventory.get(Vector2(i,j))
			if curr_slot != null:
				#TODO: Figure out how to handle split stacking later?
				if curr_slot.name == item.name and curr_slot.max_quantity < curr_slot.quantity + item.quantity:
					return Vector2(i,j)
			elif first_open == Vector2(-1,-1):
				first_open = Vector2(i,j)
	return first_open

func try_pickup_item(item : Item) -> bool:
	if inventory_count >= inventory_size.x * inventory_size.y:
		return false
	pickup_item(item)
	return true

func pickup_item(item : Item) -> void:
	var open_slot = find_open_inventory_slot(item)
	if inventory.has(open_slot):
		inventory[open_slot].quantity += item.quantity
	else:
		if inventory_count >= inventory_size.x * inventory_size.y:
			printerr("Cannot fit additional item!")
		inventory[open_slot] = item
		inventory_count+=1
	changed.emit()

func equip_item(from : Vector2, slot : Item.Slots) -> void:
	if !inventory.has(from):
		printerr("Cannot equip item from %s, it is empty!" % [from])
	
	var itm = inventory[from]
	if !itm.can_equip_slot(slot):
		printerr("Cannot equip item from %s to %s, it is the wrong type!" % [from, Item.slot_name(slot)])
	
	if !job.can_equip(itm):
		printerr("Cannot equip item from %s to %s, %s cannot equip it!" % [from, Item.slot_name(slot), job.name])
	
	if equipment[slot] != null:
		inventory[from] = equipment[slot]
		remove_stats_from_item(itm)
	else:
		inventory.erase(from)
	equipment[slot] = itm
	apply_stats_from_item(itm)
		#Apply Stats from

func remove_stats_from_item(itm : Item) -> void:
	stats.STR -= stats.STR 
	stats.DEX-= stats.DEX
	stats.INT-= stats.INT
	stats.CHA-= stats.CHA
	stats.FAI-= stats.FAI
	stats.LUK-= stats.LUK
	stats.PATK-= stats.PATK
	stats.MATK-= stats.MATK
	stats.PDEF-= stats.PDEF
	stats.MDEF-= stats.MDEF

func apply_stats_from_item(itm : Item) -> void:
	stats.STR += stats.STR 
	stats.DEX+= stats.DEX
	stats.INT+= stats.INT
	stats.CHA+= stats.CHA
	stats.FAI+= stats.FAI
	stats.LUK+= stats.LUK
	stats.PATK+= stats.PATK
	stats.MATK+= stats.MATK
	stats.PDEF+= stats.PDEF
	stats.MDEF+= stats.MDEF

func move_item(from : Vector2, to: Vector2) -> void:
	if !inventory.has(from):
		printerr("Cannot move item from %s to %s, %s is empty!" % [from, to, from])
	var itm = inventory[from]
	if inventory.has(to):
		var itm2 = inventory[to]
		inventory[from] = itm2
	else:
		inventory.erase(from)
	inventory[to] = itm
	changed.emit()

func generate_appearance(features=null) -> void:
	if features == null:
		appearance = {
			"hair":{
				"color":"",
				"type":""
			},
			"skin":{
				"color":"",
				"type":""
			},
			"eyes":{
				"color":"",
				"type":""
			}
		}
		#TODO: Handle different types of hair and eyes
		appearance.hair.color = AdventurerPortrait.random_color(ColorVariant.Types.HAIR)
		#appearance.hair_type = randi_range(0,len(job.portrait.hair_types) - 1)
		appearance.eyes.color = AdventurerPortrait.random_color(ColorVariant.Types.EYES)
		#appearance.eye_type = randi_range(0,len(job.portrait.eye_types) - 1)
		appearance.skin.color = AdventurerPortrait.random_color(ColorVariant.Types.SKIN)
	changed.emit()
