local MakePlayerCharacter = require "prefabs/player_common"

local assets = {
        Asset( "ANIM", "anim/player_basic.zip" ),
        Asset( "ANIM", "anim/player_idles_shiver.zip" ),
        Asset( "ANIM", "anim/player_actions.zip" ),
        Asset( "ANIM", "anim/player_actions_axe.zip" ),
        Asset( "ANIM", "anim/player_actions_pickaxe.zip" ),
        Asset( "ANIM", "anim/player_actions_shovel.zip" ),
        Asset( "ANIM", "anim/player_actions_blowdart.zip" ),
        Asset( "ANIM", "anim/player_actions_eat.zip" ),
        Asset( "ANIM", "anim/player_actions_item.zip" ),
        Asset( "ANIM", "anim/player_actions_uniqueitem.zip" ),
        Asset( "ANIM", "anim/player_actions_bugnet.zip" ),
        Asset( "ANIM", "anim/player_actions_fishing.zip" ),
        Asset( "ANIM", "anim/player_actions_boomerang.zip" ),
        Asset( "ANIM", "anim/player_bush_hat.zip" ),
        Asset( "ANIM", "anim/player_attacks.zip" ),
        Asset( "ANIM", "anim/player_idles.zip" ),
        Asset( "ANIM", "anim/player_rebirth.zip" ),
        Asset( "ANIM", "anim/player_jump.zip" ),
        Asset( "ANIM", "anim/player_amulet_resurrect.zip" ),
        Asset( "ANIM", "anim/player_teleport.zip" ),
        Asset( "ANIM", "anim/wilson_fx.zip" ),
        Asset( "ANIM", "anim/player_one_man_band.zip" ),
        Asset( "ANIM", "anim/shadow_hands.zip" ),
        Asset( "SOUND", "sound/sfx.fsb" ),
        Asset( "SOUND", "sound/wilson.fsb" ),
        Asset( "ANIM", "anim/beard.zip" ),
        --custom assets
        Asset( "ANIM", "anim/willette.zip" ),
		Asset( "ANIM", "anim/willettemonster.zip" ),
}
local prefabs = {}

local start_inv =
{
"flowerhat",
}

function GetSpecialCharacterString(character) --describe text override for monster mode
    character = string.lower(character)
    if character == "willette" and GetPlayer().IsMonster==true then
		return TUNING.WILLETTE_MONSTERSPEECH[math.random(#TUNING.WILLETTE_MONSTERSPEECH)]
    end
end	

local function SayRandom(sayings) --say randomized things from a table of stuff
    return sayings[math.random(#sayings)]
end

local function StopMonsterDecrement(inst) --reset monster progress and stop monster progress decrementer to avoid negative progress
    if inst.DecrementerIsRunning then
        if TUNING.WILLETTE_DEBUG==true then print ("Progress cleared, decrementer cancelled") end
        inst.MonsterEaten=0
        inst.DecrementerIsRunning:Cancel()
        inst.DecrementerIsRunning=nil
    end
end

--tuning follows, variables are in /scripts/willete_tuning.lua
local function NormalStats(inst) --normal mode tuning
	if inst.IsMonster==true then
		inst.components.talker:Say(SayRandom(TUNING.WILLETTE_UNMONSTER))
		if TUNING.WILLETTE_DEBUG==true then print ("Monster mode deactivated") end
	end
    StopMonsterDecrement(inst)
	inst.IsMonster=false
	inst.AnimState:SetBuild("willette")
	--health
	inst.RetainHealth=inst.components.health.currenthealth
	inst.components.health:SetMaxHealth(TUNING.WILLETTE_NORMAL_HEALTH)
	inst.components.health.currenthealth=inst.RetainHealth
	inst.components.health:DoDelta(0, true)
	--hunger
	inst.RetainHunger=inst.components.hunger.current
	inst.components.hunger:SetMax(TUNING.WILLETTE_NORMAL_HUNGER_MAX)
	inst.components.hunger.current=inst.RetainHunger
	inst.components.hunger:SetRate(TUNING.WILLETTE_NORMAL_HUNGER_RATE)
	--sanity
	inst.RetainSanity=inst.components.sanity.current
	inst.components.sanity:SetMax(TUNING.WILLETTE_NORMAL_SANITY)
	inst.components.sanity.current=inst.RetainSanity
	--other
	inst.components.locomotor.walkspeed=TUNING.WILLETTE_NORMAL_WALK_SPEED
	inst.components.locomotor.runspeed=TUNING.WILLETTE_NORMAL_RUN_SPEED
	inst.components.combat.damagemultiplier=TUNING.WILLETTE_NORMAL_ATTACKMULT
	inst.components.sanity.night_drain_mult=TUNING.WILLETTE_NORMAL_NIGHTDRAIN
	inst.components.sanity.neg_aura_mult=TUNING.WILLETTE_NORMAL_NEGAURAMULT
	inst.components.sanity.dapperness=TUNING.WILLETTE_NORMAL_DAPPERNESS
	inst:DoTaskInTime(3, function() inst.Light:Enable(false) end)
end

local function MonsterStats(inst) --monster mode tuning
	inst.IsMonster=true
	inst.AnimState:SetBuild("willettemonster")
	--health
	inst.RetainHealth=inst.components.health.currenthealth
	inst.components.health:SetMaxHealth(TUNING.WILLETTE_MONSTER_HEALTH)
	inst.components.health.currenthealth=inst.RetainHealth
	inst.components.health:DoDelta(0, true)
	--hunger
	inst.RetainHunger=inst.components.hunger.current
	inst.components.hunger:SetMax(TUNING.WILLETTE_MONSTER_HUNGER_MAX)
	inst.components.hunger.current=inst.RetainHunger
	inst.components.hunger:SetRate(TUNING.WILLETTE_MONSTER_HUNGER_RATE)
	--sanity
	inst.RetainSanity=inst.components.sanity.current
	inst.components.sanity:SetMax(TUNING.WILLETTE_MONSTER_SANITY)
	inst.components.sanity.current=inst.RetainSanity
	--other
	inst.components.locomotor.walkspeed=TUNING.WILLETTE_MONSTER_WALK_SPEED
	inst.components.locomotor.runspeed=TUNING.WILLETTE_MONSTER_RUN_SPEED
	inst.components.combat.damagemultiplier=TUNING.WILLETTE_MONSTER_ATTACKMULT
	inst.components.sanity.night_drain_mult=TUNING.WILLETTE_MONSTER_NIGHTDRAIN
	inst.components.sanity.neg_aura_mult=TUNING.WILLETTE_MONSTER_NEGAURAMULT
	inst.components.sanity.dapperness=TUNING.WILLETTE_MONSTER_DAPPERNESS
	inst.Light:Enable(true)
end
--end tuning

local function TurnMonster(inst) --run a bunch of sound/animation stuff and apply monster mode stats
    StopMonsterDecrement(inst)
    inst.MonsterEatenThresh=math.random(TUNING.WILLETTE_MONSTER_THRESHMIN,TUNING.WILLETTE_MONSTER_THRESHMAX)
	if TUNING.WILLETTE_DEBUG==true then print ("Monster mode activated, new threshhold="..inst.MonsterEatenThresh) end
    inst.HUD:Hide()
	inst.sg:GoToState("death")
	inst.SoundEmitter:PlaySound("dontstarve/wilson/death")
    inst.SoundEmitter:PlaySound("dontstarve/characters/willow/death_voice")
    inst:DoTaskInTime(2, function()
        TheFrontEnd:Fade(false,1)
        inst:DoTaskInTime(1.2, function()
            TheFrontEnd:Fade(true,1)
            inst.AnimState:PlayAnimation("amulet_rebirth")
            inst.SoundEmitter:PlaySound("dontstarve/common/rebirth_amulet_raise")
            inst.AnimState:OverrideSymbol("FX", "player_amulet_resurrect", "FX")
            TheCamera:SetDistance(14)
            inst:DoTaskInTime(2.5, function() 
                MonsterStats(inst)
                inst.SoundEmitter:PlaySound("dontstarve/common/rebirth_amulet_poof")
            end)
            inst:DoTaskInTime(3.5, function()
                inst.sg:GoToState("idle")
                inst.components.sanity:DoDelta(TUNING.WILLETTE_MONSTER_SANITYPENALTY)
                inst.components.talker:Say(SayRandom(TUNING.WILLETTE_TURNMONSTER))
                inst.HUD:Show() 
            end)
        end)
    end)
end

local function KnockOut(inst) --transformation fails, wake up next day
    StopMonsterDecrement(inst)
    inst.MonsterEatenThresh=math.random(TUNING.WILLETTE_MONSTER_THRESHMIN,TUNING.WILLETTE_MONSTER_THRESHMAX)
    if TUNING.WILLETTE_DEBUG==true then print ("Knockout activated, new threshhold="..inst.MonsterEatenThresh) end
    inst.HUD:Hide()
    inst.sg:GoToState("death")
	inst.SoundEmitter:PlaySound("dontstarve/wilson/death")
    inst.SoundEmitter:PlaySound("dontstarve/characters/willow/death_voice")
    inst:DoTaskInTime(2, function()
        TheFrontEnd:Fade(false,1)
        inst:DoTaskInTime(1.2, function()
            TheFrontEnd:Fade(true,1)
            GetClock():MakeNextDay()        
            inst.sg:GoToState("wakeup")
            inst.components.talker:Say(SayRandom(TUNING.WILLETTE_KNOCKEDOUT))
            inst.HUD:Show()
        end)
    end)
end

local function MonsterCheck(inst) -- if over threshold, transform. maybe knock out instead
    if inst.MonsterEaten >= inst.MonsterEatenThresh then
        inst.KnockOutChance=math.random()
        if inst.KnockOutChance>TUNING.WILLETTE_MONSTER_KNOCKCHANCE then
            TurnMonster(inst)
        elseif inst.KnockOutChance<=TUNING.WILLETTE_MONSTER_KNOCKCHANCE then
            KnockOut(inst)
        end
    end
end

local function WereMonster(inst) --transform if full moon
    if GetClock():GetMoonPhase()=="full" and inst.IsMonster==false then
        TurnMonster(inst)
    end
end

local function MonsterDecrement(inst) --wait, then decrement monster progress by 1, repeat if > 0
    if inst.DecrementerIsRunning then return end
    if inst.MonsterEaten>0 then
        inst.DecTime=math.random(TUNING.WILLETTE_MONSTER_DECMIN,TUNING.WILLETTE_MONSTER_DECMAX)
        inst.DecrementerIsRunning=inst:DoTaskInTime(inst.DecTime, function()
            inst.MonsterEaten=inst.MonsterEaten-1
            if TUNING.WILLETTE_DEBUG==true then print ("Monster progress decremented: "..inst.MonsterEaten.."/"..(inst.MonsterEatenThresh)..", delay="..inst.DecTime) end
            inst.DecrementerIsRunning=nil
            MonsterDecrement(inst) --RECURSION!!!
        end)
    end
end

local function MonsterIncrement(inst, food) --increment monster progress by 1, check if threshhold exceeded, start decrement timer
	if inst.IsMonster==true then return end
	if GetClock():IsDay() then return end
	if string.find(tostring(food),"monster") then
		if inst.MonsterEaten < inst.MonsterEatenThresh then
			inst.MonsterEaten=inst.MonsterEaten+1
			if TUNING.WILLETTE_DEBUG==true then print ("Monster progress incremented: "..inst.MonsterEaten.."/"..inst.MonsterEatenThresh) end
            MonsterCheck(inst)
			MonsterDecrement(inst)
		end
	end
end

local function OnSave(inst, data) --save this data to savefile
	data.IsMonster = inst.IsMonster
	data.MonsterEaten = inst.MonsterEaten
	data.RetainHealth = inst.components.health.currenthealth --we need this because the health functions are dumb :P
end

local function OnLoad(inst, data) --load this data from savefile on new session start
	if data then
		if data.IsMonster==true then
			MonsterStats(inst)
		end
		inst.MonsterEaten = data.MonsterEaten or 0
		inst.components.health:SetVal(inst.components.health.currenthealth or TUNING.WILLETTE_NORMAL_HEALTH)
		inst.components.health:DoDelta(0, true)
	end
end

local fn = function(inst)
	--define vars
    inst.IsMonster=false
	inst.MonsterEaten=0
    inst.DecrementerIsRunning=nil
	inst.MonsterEatenThresh=math.random(TUNING.WILLETTE_MONSTER_THRESHMIN,TUNING.WILLETTE_MONSTER_THRESHMAX)
    inst.KnockOutChance=0
	inst.RetainHealth=TUNING.WILLETTE_NORMAL_HEALTH
	inst.RetainHunger=TUNING.WILLETTE_NORMAL_HUNGER_MAX
	inst.RetainSanity=TUNING.WILLETTE_NORMAL_SANITY

	--tuning for first run
	inst.components.health:SetMaxHealth(TUNING.WILLETTE_NORMAL_HEALTH)
	inst.components.hunger:SetMax(TUNING.WILLETTE_NORMAL_HUNGER_MAX)
	inst.components.hunger:SetRate(TUNING.WILLETTE_NORMAL_HUNGER_RATE)
	inst.components.sanity:SetMax(TUNING.WILLETTE_NORMAL_SANITY)
	inst.components.locomotor.walkspeed=TUNING.WILLETTE_NORMAL_WALK_SPEED
	inst.components.locomotor.runspeed=TUNING.WILLETTE_NORMAL_RUN_SPEED
	inst.components.combat.damagemultiplier=TUNING.WILLETTE_NORMAL_ATTACKMULT
	inst.components.sanity.night_drain_mult=TUNING.WILLETTE_NORMAL_NIGHTDRAIN
	inst.components.sanity.neg_aura_mult=TUNING.WILLETTE_NORMAL_NEGAURAMULT
	inst.components.sanity.dapperness=TUNING.WILLETTE_NORMAL_DAPPERNESS

	--speech sound and map icon
	inst.soundsname = "willow"
	inst.MiniMapEntity:SetIcon( "wilson.png" )
	
	--monster mode triggers
	inst.components.eater:SetOnEatFn(MonsterIncrement)
	inst:ListenForEvent("daycomplete", function() NormalStats(inst) end, GetWorld())
    inst:ListenForEvent("nighttime", function() WereMonster(inst) end, GetWorld())
	
	--monster-o-vision
	inst.entity:AddLight()
    inst.Light:Enable(false)
	inst.Light:SetRadius(5)
    inst.Light:SetFalloff(.5)
    inst.Light:SetIntensity(.6)
    inst.Light:SetColour(245/255,40/255,0/255)
	
	--save/load
	inst.OnSave=OnSave 
    inst.OnLoad=OnLoad
end

STRINGS.CHARACTER_TITLES.willette = "The Follower"
STRINGS.CHARACTER_NAMES.willette = "Willette"
STRINGS.CHARACTER_DESCRIPTIONS.willette ="*Is dapper\n*Is monstrous"
STRINGS.CHARACTER_QUOTES.willette = "\"Need any help?\""
STRINGS.CHARACTERS.WILLETTE=require "speech_willette"

return MakePlayerCharacter("willette", prefabs, assets, fn, start_inv)