-- ToME - Tales of Maj'Eyal
-- Copyright (C) 2009, 2010, 2011 Nicolas Casalini
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- 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 General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
--
-- Nicolas Casalini "DarkGod"
-- darkgod@te4.org

require "engine.class"
local Dialog = require "engine.ui.Dialog"
local Textzone = require "engine.ui.Textzone"
local Separator = require "engine.ui.Separator"
local List = require "engine.ui.List"
local Savefile = require "engine.Savefile"
local Map = require "engine.Map"

module(..., package.seeall, class.inherit(Dialog))

function _M:init(actor)
	self.actor = actor
	Dialog.init(self, "Death!", 500, 300)

	actor:saveUUID()

	self:generateList()
	if self.dont_show then return end
	if not config.settings.cheat then game:saveGame() end

	self.c_desc = Textzone.new{width=self.iw, auto_height=true, text=[[You have #LIGHT_RED#died#LAST#!
Death in ToME is usually permanent, but if you have a means of resurrection it will be proposed in the menu below.
You can dump your character data to a file to remember her/him forever, or you can exit and try once again to survive in the wilds!
]]}
	self.c_list = List.new{width=self.iw, nb_items=#self.list, list=self.list, fct=function(item) self:use(item) end}

	self:loadUI{
		{left=0, top=0, ui=self.c_desc},
		{left=5, top=self.c_desc.h, padding_h=10, ui=Separator.new{dir="vertical", size=self.iw - 10}},
		{left=0, bottom=0, ui=self.c_list},
	}
	self:setFocus(self.c_list)
	self:setupUI(false, true)
end

--- Clean the actor from debuffs/buffs
function _M:cleanActor(actor)
	local effs = {}

	-- Remove chronoworlds
	if game._chronoworlds then game._chronoworlds = nil end

	-- Go through all spell effects
	for eff_id, p in pairs(actor.tmp) do
		local e = actor.tempeffect_def[eff_id]
		effs[#effs+1] = {"effect", eff_id}
	end

	-- Go through all sustained spells
	for tid, act in pairs(actor.sustain_talents) do
		if act then
			effs[#effs+1] = {"talent", tid}
		end
	end

	while #effs > 0 do
		local eff = rng.tableRemove(effs)

		if eff[1] == "effect" then
			actor:removeEffect(eff[2])
		else
			actor:forceUseTalent(eff[2], {ignore_energy=true, no_equilibrium_fail=true, no_paradox_fail=true})
		end
	end
end

--- Restore resources
function _M:restoreResources(actor)
	if actor.resetToFull then
		actor:resetToFull()
		actor.energy.value = game.energy_to_act
	end
end

--- Basic resurrection
function _M:resurrectBasic(actor)
	actor.dead = false
	actor.died = (actor.died or 0) + 1

	-- Find the position of the last dead
	local last = game.party:findLastDeath()

	local x, y = util.findFreeGrid(last.x, last.y, 20, true, {[Map.ACTOR]=true})
	if not x then x, y = last.x, last.y end

	actor.x, actor.y = nil, nil
	actor:move(x, y, true)

	game.level:addEntity(actor)
	game:unregisterDialog(self)
	game.level.map:redisplay()
	actor.energy.value = game.energy_to_act
	
	-- apply cursed equipment
	if actor.hasTalent and actor.hasTalent(actor.T_DEFILING_TOUCH) then
		local t = self:getTalentFromId(self.T_DEFILING_TOUCH)
		t.updateCurses(self, t, true)
	end
	
	actor.changed = true
	game.paused = true
end

--- Send the party to the Eidolon Plane
function _M:eidolonPlane()
	game:onTickEnd(function()
		if not self.actor:attr("infinite_lifes") then
			self.actor:attr("easy_mode_lifes", -1)
			game.log("#LIGHT_RED#You have %s left.", (self.actor:attr("easy_mode_lifes") and self.actor:attr("easy_mode_lifes").." life(s)") or "no more lives")
		end

		self:cleanActor(self.actor)
		self:resurrectBasic(self.actor)
		for uid, e in pairs(game.level.entities) do
			self:restoreResources(e)
		end

		local oldzone = game.zone
		local oldlevel = game.level
		local zone = engine.Zone.new("eidolon-plane")
		local level = zone:getLevel(game, 1, 0)

		level.data.eidolon_exit_x = self.actor.x
		level.data.eidolon_exit_y = self.actor.y

		local acts = {}
		for act, _ in pairs(game.party.members) do
			if not act.dead then
				acts[#acts+1] = act
				if oldlevel:hasEntity(act) then oldlevel:removeEntity(act) end
			end
		end

		level.source_zone = oldzone
		level.source_level = oldlevel
		game.zone = zone
		game.level = level
		game.zone_name_s = nil

		for _, act in ipairs(acts) do
			local x, y = util.findFreeGrid(5, 5, 20, true, {[Map.ACTOR]=true})
			if x then
				level:addEntity(act)
				act:move(x, y, true)
				act.changed = true
				game.level.map:particleEmitter(x, y, 1, "teleport")
			end
		end

		for uid, act in pairs(game.level.entities) do
			if act.setEffect then
				if game.level.data.zero_gravity then act:setEffect(act.EFF_ZERO_GRAVITY, 1, {})
				else act:removeEffect(act.EFF_ZERO_GRAVITY, nil, true) end
			end
		end

		game.log("#LIGHT_RED#From the brink of death you seem to be yanked to another plane.")
		game.player:updateMainShader()
		if not config.settings.cheat then game:saveGame() end
	end)
	return true
end

function _M:use(item)
	if not item then return end
	local act = item.action

	if act == "exit" then
		if item.subaction == "none" then
			util.showMainMenu()
		elseif item.subaction == "restart" then
			util.showMainMenu(false, engine.version[4], engine.version[1].."."..engine.version[2].."."..engine.version[3], game.__mod_info.short_name, game.save_name, true, ("auto_quickbirth=%q"):format(game:getPlayer(true).name))
		elseif item.subaction == "restart-new" then
			util.showMainMenu(false, engine.version[4], engine.version[1].."."..engine.version[2].."."..engine.version[3], game.__mod_info.short_name, game.save_name, true)
		end
	elseif act == "dump" then
		game:registerDialog(require("mod.dialogs.CharacterSheet").new(self.actor))
	elseif act == "log" then
		game:registerDialog(require("mod.dialogs.ShowChatLog").new("Message Log", 0.6, game.logdisplay, profile.chat))
	elseif act == "cheat" then
		game.logPlayer(self.actor, "#LIGHT_BLUE#You resurrect! CHEATER!")

		self:cleanActor(self.actor)
		self:resurrectBasic(self.actor)
		self:restoreResources(self.actor)
	elseif act == "blood_life" then
		self.actor.blood_life = false
		game.logPlayer(self.actor, "#LIGHT_RED#The Blood of Life rushes through your dead body. You come back to life!")

		self:cleanActor(self.actor)
		self:resurrectBasic(self.actor)
		self:restoreResources(self.actor)
		world:gainAchievement("UNSTOPPABLE", actor)
	elseif act == "lichform" then
		local t = self.actor:getTalentFromId(self.actor.T_LICHFORM)

		self:cleanActor(self.actor)
		self:resurrectBasic(self.actor)
		self:restoreResources(self.actor)
		world:gainAchievement("LICHFORM", actor)
		t.becomeLich(self.actor, t)
		self.actor:updateModdableTile()
	elseif act == "easy_mode" then
		self:eidolonPlane()
	elseif act == "skeleton" then
		self.actor:attr("re-assembled", 1)
		game.logPlayer(self.actor, "#YELLOW#Your bones magically knit back together. You are once more able to dish out pain to your foes!")

		self:cleanActor(self.actor)
		self:resurrectBasic(self.actor)
		self:restoreResources(self.actor)
		world:gainAchievement("UNSTOPPABLE", actor)
	elseif act:find("^consume") then
		local inven, item, o = item.inven, item.item, item.object
		self.actor:removeObject(inven, item)
		game.logPlayer(self.actor, "#YELLOW#Your %s is consumed and disappears! You come back to life!", o:getName{do_colour=true})

		self:cleanActor(self.actor)
		self:resurrectBasic(self.actor)
		self:restoreResources(self.actor)
		world:gainAchievement("UNSTOPPABLE", actor)
	end
end

function _M:generateList()
	local list = {}
	local allow_res = true

	-- Pause the game
	game:onTickEnd(function()
		game.paused = true
		game.player.energy.value = game.energy_to_act
	end)

	if game.zone.is_eidolon_plane then
		game.logPlayer(self, "You managed to die on the eidolon plane! DIE!")
		game:onTickEnd(function() world:gainAchievement("EIDOLON_DEATH", self.actor) end)
		allow_res = false
	end

	if config.settings.cheat then list[#list+1] = {name="Resurrect by cheating", action="cheat"} end
	if not self.actor.no_resurrect and allow_res then
		if self.actor:isTalentActive(self.actor.T_LICHFORM) then
			self:use{action="lichform"}
			self.dont_show = true
			return
		end
		if self.actor:attr("easy_mode_lifes") or self.actor:attr("infinite_lifes") then
			self:use{action="easy_mode"}
			self.dont_show = true
			return
		end
		if self.actor:attr("blood_life") and not self.actor:attr("undead") then list[#list+1] = {name="Resurrect with the Blood of Life", action="blood_life"} end
		if self.actor:getTalentLevelRaw(self.actor.T_SKELETON_REASSEMBLE) >= 5 and not self.actor:attr("re-assembled") then list[#list+1] = {name="Re-assemble your bones and resurrect (Skeleton ability)", action="skeleton"} end

		local consumenb = 1
		self.actor:inventoryApplyAll(function(inven, item, o)
			if o.one_shot_life_saving and (not o.slot or inven.worn) then
				list[#list+1] = {name="Resurrect by consuming "..o:getName{do_colour=true}, action="consume"..consumenb, inven=inven, item=item, object=o}
				consumenb = consumenb + 1
			end
		end)
	end

	list[#list+1] = {name=(not profile.auth and "Message Log" or "Message/Chat log (allows to talk)"), action="log"}
	list[#list+1] = {name="Character dump", action="dump"}
	list[#list+1] = {name="Restart the same character", action="exit", subaction="restart"}
	list[#list+1] = {name="Restart with a new character", action="exit", subaction="restart-new"}
	list[#list+1] = {name="Exit to main menu", action="exit", subaction="none"}

	self.list = list
end
