require "brains/houndbrain"
require "stategraphs/SGhound"

local trace = function() end

local assets=
{
	Asset("ANIM", "anim/hound_lightning.zip"),
	Asset("SOUND", "sound/hound.fsb"),
}

local prefabs =
{
	"monstermeat",
	"houndstooth",
	"goldnugget",
	"yellowgem",
}

local WAKE_TO_FOLLOW_DISTANCE = 8
local SLEEP_NEAR_HOME_DISTANCE = 10
local SHARE_TARGET_DIST = 30
local HOME_TELEPORT_DIST = 30

local function ShouldWakeUp(inst)
    return DefaultWakeTest(inst) or (inst.components.follower and inst.components.follower.leader and not inst.components.follower:IsNearLeader(WAKE_TO_FOLLOW_DISTANCE))
end

local function ShouldSleep(inst)
    return inst:HasTag("pet_hound")
    and not GetClock():IsDay()
    and not (inst.components.combat and inst.components.combat.target)
    and not (inst.components.burnable and inst.components.burnable:IsBurning() )
    and (not inst.components.homeseeker or inst:IsNear(inst.components.homeseeker.home, SLEEP_NEAR_HOME_DISTANCE))
end

local function OnNewTarget(inst, data)
    if inst.components.sleeper:IsAsleep() then
        inst.components.sleeper:WakeUp()
    end
end

local function retargetfn(inst)
    local dist = TUNING.HOUND_TARGET_DIST
    if inst:HasTag("pet_hound") then
        dist = TUNING.HOUND_FOLLOWER_TARGET_DIST
    end
    return FindEntity(inst, dist, function(guy) 
		return not guy:HasTag("wall") and not guy:HasTag("houndmound") and not (guy:HasTag("hound") or guy:HasTag("houndfriend")) and inst.components.combat:CanTarget(guy)
    end)
end

local function KeepTarget(inst, target)
    return inst.components.combat:CanTarget(target) and (not inst:HasTag("pet_hound") or inst:IsNear(target, TUNING.HOUND_FOLLOWER_TARGET_KEEP))
end

local function OnAttacked(inst, data)
    inst.components.combat:SetTarget(data.attacker)
    inst.components.combat:ShareTarget(data.attacker, SHARE_TARGET_DIST, function(dude) return dude:HasTag("hound") or dude:HasTag("houndfriend") and not dude.components.health:IsDead() end, 5)
	if data.attacker.components.health then
		if math.random() < 0.25 and (data.weapon == nil or (not data.weapon:HasTag("projectile") and data.weapon.projectile == nil)) and (data.attacker ~= GetPlayer() or (data.attacker == GetPlayer() and not GetPlayer().components.inventory:IsInsulated())) then
			data.attacker.components.health:DoDelta(-5)
			if data.attacker == GetPlayer() then		
				if (IsDLCEnabled(CAPY_DLC) or IsDLCEnabled(REIGN_OF_GIANTS)) then
					data.attacker.sg:GoToState("electrocute")
				end
			end
		end
	end
end

local function OnAttackOther(inst, data)
    inst.components.combat:ShareTarget(data.target, SHARE_TARGET_DIST, function(dude) return dude:HasTag("hound") or dude:HasTag("houndfriend") and not dude.components.health:IsDead() end, 5)
end

local function GetReturnPos(inst)
    local rad = 2
    local pos = inst:GetPosition()
    trace("GetReturnPos", inst, pos)
    local angle = math.random()*2*PI
    pos = pos + Point(rad*math.cos(angle), 0, -rad*math.sin(angle))
    trace("    ", pos)
    return pos:Get()
end

local function DoReturn(inst)
    if inst.components.homeseeker and inst.components.homeseeker:HasHome()  then
        if inst:HasTag("pet_hound") then
            if inst.components.homeseeker.home:IsAsleep() and not inst:IsNear(inst.components.homeseeker.home, HOME_TELEPORT_DIST) then
                local x, y, z = GetReturnPos(inst.components.homeseeker.home)
                inst.Physics:Teleport(x, y, z)
                trace("hound warped home", x, y, z)
            end
        elseif inst.components.homeseeker.home.components.childspawner then
            inst.components.homeseeker.home.components.childspawner:GoHome(inst)
        end
    end
end

local function OnNight(inst)
    if inst:IsAsleep() then
        DoReturn(inst)  
    end
end

local function OnEntitySleep(inst)
    if not GetClock():IsDay() then
        DoReturn(inst)
    end
end

local function OnSave(inst, data)
    data.ispet = inst:HasTag("pet_hound")
end
        
local function OnLoad(inst, data)
    if data and data.ispet then
        inst:AddTag("pet_hound")
        inst:AddComponent("follower")
        if inst.sg then
            inst.sg:GoToState("idle")
        end
    end
end

local function LightsOff(inst)
	inst.Light:Enable(false)
	inst.lightson = false
	inst.AnimState:SetBloomEffectHandle("")
end

local function LightsOn(inst)
	inst.Light:Enable(true)
	inst.lightson = true
	inst.AnimState:SetBloomEffectHandle("shaders/anim.ksh")
end

local function CheckTime(inst)
	if GetClock():IsDay() then
		LightsOff(inst)
	elseif GetClock():IsDusk() then
		LightsOff(inst)
	elseif GetClock():IsNight() then
		LightsOn(inst)
	end
end

local function fn(Sim)
	local inst = CreateEntity()
	local trans = inst.entity:AddTransform()
	local anim = inst.entity:AddAnimState()
    local physics = inst.entity:AddPhysics()
	local sound = inst.entity:AddSoundEmitter()
	local shadow = inst.entity:AddDynamicShadow()
	shadow:SetSize( 2.5, 1.5 )
	MakeCharacterPhysics(inst, 10, .5)
    inst.Transform:SetFourFaced()
     
    anim:SetBank("hound")
    anim:SetBuild("hound_lightning")
    anim:PlayAnimation("idle")
	
	local light = inst.entity:AddLight()
    inst.Light:Enable(false)
	inst.Light:SetRadius(2.0)
    inst.Light:SetFalloff(0.7)
    inst.Light:SetIntensity(.65)
    inst.Light:SetColour(223/255, 208/255, 69/255)
	inst.AnimState:SetBloomEffectHandle("shaders/anim.ksh")
	
	inst:AddTag("scarytoprey")
    inst:AddTag("monster")
    inst:AddTag("hostile")
    inst:AddTag("hound")
	
    inst:AddComponent("locomotor")
    inst.components.locomotor.runspeed = TUNING.HOUND_SPEED
	
    inst:AddComponent("eater")
    inst.components.eater:SetCarnivore()
	inst.components.eater:SetCanEatHorrible()
    inst.components.eater.strongstomach = true
    
    inst:AddComponent("health")
    inst.components.health:SetMaxHealth(200)
    
    inst:AddComponent("sanityaura")
    inst.components.sanityaura.aura = -TUNING.SANITYAURA_MED   
	
	inst:AddComponent("follower")
    
    inst:AddComponent("combat")
    inst.components.combat:SetDefaultDamage(30)
    inst.components.combat:SetAttackPeriod(3)
    inst.components.combat:SetRetargetFunction(3, retargetfn)
    inst.components.combat:SetKeepTargetFunction(KeepTarget)
    inst.components.combat:SetHurtSound("dontstarve/creatures/hound/hurt")

    inst:AddComponent("lootdropper")
	inst.components.lootdropper:SetLoot({"monstermeat","goldnugget","houndstooth"})
	inst.components.lootdropper:AddChanceLoot("yellowgem",0.01)
    
    inst:AddComponent("inspectable")
    
    inst:AddComponent("sleeper")
    inst.components.sleeper:SetResistance(3)
    inst.components.sleeper.testperiod = GetRandomWithVariance(6, 2)
    inst.components.sleeper:SetSleepTest(ShouldSleep)
    inst.components.sleeper:SetWakeTest(ShouldWakeUp)
	
	local brain = require "brains/houndbrain"
    inst:SetBrain(brain)
	inst:SetStateGraph("SGhound")
	
    inst.OnEntitySleep = OnEntitySleep

    inst.OnSave = OnSave
    inst.OnLoad = OnLoad
	
	MakeMediumFreezableCharacter(inst, "hound_body")
    MakeMediumBurnableCharacter(inst, "hound_body")
	
	CheckTime(inst)
	inst:ListenForEvent("daytime", function() CheckTime(inst) end , GetWorld())
	inst:ListenForEvent("dusktime", function() CheckTime(inst) OnNight(inst) end , GetWorld())
	inst:ListenForEvent("nighttime", function() CheckTime(inst) OnNight(inst) end , GetWorld())
	inst:ListenForEvent("newcombattarget", OnNewTarget)
    inst:ListenForEvent("attacked", OnAttacked)
    inst:ListenForEvent("onattackother", OnAttackOther)
	inst:ListenForEvent("death", function(inst) local pt = Point(inst.Transform:GetWorldPosition()) GetSeasonManager():DoLightningStrike(pt) end)
    return inst
end

STRINGS.NAMES.LIGHTNINGHOUND = "Lightning Hound"
STRINGS.CHARACTERS.GENERIC.DESCRIBE.LIGHTNINGHOUND = "Will the lightning if he bits me?"

return Prefab("monsters/lightninghound", fn, assets, prefabs)