require "prefabutil"

local assets =
{
    Asset("ANIM", "anim/spider_cocoon.zip"),
    Asset("SOUND", "sound/spider.fsb"),
	Asset("MINIMAP_IMAGE", "spiderden"), --shared with spiderden2 and 3
}

local prefabs =
{
    "spider",
    "spider_warrior",
    "silk",
    "spidereggsack",
    "spiderqueen",
}

local ANIM_DATA =
{
    SMALL =
    {
        hit = "cocoon_small_hit",
        idle = "cocoon_small",
        init = "grow_sac_to_small",
        freeze = "frozen_small",
        thaw = "frozen_loop_pst_small",
    },

    MEDIUM =
    {
        hit = "cocoon_medium_hit",
        idle = "cocoon_medium",
        init = "grow_small_to_medium",
        freeze = "frozen_medium",
        thaw = "frozen_loop_pst_medium",
    },

    LARGE =
    {
        hit = "cocoon_large_hit",
        idle = "cocoon_large",
        init = "grow_medium_to_large",
        freeze = "frozen_large",
        thaw = "frozen_loop_pst_large",
    },
}

local LOOT_DATA =
{
    SMALL = { "silk", "silk" },
    MEDIUM = { "silk", "silk", "silk", "silk" },
    LARGE = { "silk", "silk", "silk", "silk", "silk", "silk", "spiderhome" },
}

local function SetStage(inst, stage)
    if stage <= 3 then
        inst.SoundEmitter:PlaySound("dontstarve/creatures/spider/spiderLair_grow")
        --inst.components.health:SetMaxHealth(TUNING.SPIDERDEN_HEALTH[stage])
		if MOBPVPMODE == "Enable" and inst.components.childspawner ~= nil then
		inst.components.childspawner:SetMaxChildren(math.floor(SpringCombatMod(TUNING.SPIDERDEN_SPIDERS[stage])))
        inst.components.childspawner:SetMaxEmergencyChildren(TUNING.SPIDERDEN_EMERGENCY_WARRIORS[stage])
        inst.components.childspawner:SetEmergencyRadius(TUNING.SPIDERDEN_EMERGENCY_RADIUS[stage])
		end
        inst.components.health:SetMaxHealth(TUNING.SPIDERDEN_HEALTH[stage])
		
        inst.AnimState:PlayAnimation(inst.anims.init)
        inst.AnimState:PushAnimation(inst.anims.idle, true)
    end

    inst.components.upgradeable:SetStage(stage)
    inst.data.stage = stage -- track here, as growable component may go away
end

local function SetSmall(inst)
    inst.anims = ANIM_DATA.SMALL
    SetStage(inst, 1)
    inst.components.lootdropper:SetLoot(LOOT_DATA.SMALL)

    if inst.components.burnable ~= nil then
        inst.components.burnable:SetFXLevel(3)
        inst.components.burnable:SetBurnTime(20)
    end

    if inst.components.freezable ~= nil then
        inst.components.freezable:SetShatterFXLevel(3)
        inst.components.freezable:SetResistance(2)
    end

    inst.GroundCreepEntity:SetRadius(5)
end

local function SetMedium(inst)
    inst.anims = ANIM_DATA.MEDIUM
    SetStage(inst, 2)
    inst.components.lootdropper:SetLoot(LOOT_DATA.MEDIUM)

    if inst.components.burnable ~= nil then
        inst.components.burnable:SetFXLevel(3)
        inst.components.burnable:SetBurnTime(20)
    end

    if inst.components.freezable ~= nil then
        inst.components.freezable:SetShatterFXLevel(4)
        inst.components.freezable:SetResistance(3)
    end

    inst.GroundCreepEntity:SetRadius(9)
end

local function SetLarge(inst)
    inst.anims = ANIM_DATA.LARGE
    SetStage(inst, 3)
    inst.components.lootdropper:SetLoot(LOOT_DATA.LARGE)

    if inst.components.burnable ~= nil then
        inst.components.burnable:SetFXLevel(4)
        inst.components.burnable:SetBurnTime(30)
    end

    if inst.components.freezable ~= nil then
        inst.components.freezable:SetShatterFXLevel(5)
        inst.components.freezable:SetResistance(4)
    end

    inst.GroundCreepEntity:SetRadius(9)
end

local function PlayLegBurstSound(inst)
    inst.SoundEmitter:PlaySound("dontstarve/creatures/spiderqueen/legburst")
end

local function GetSmallGrowTime(inst)
    return TUNING.SPIDERDEN_GROW_TIME[1] * (1 + math.random())
end

local function GetMedGrowTime(inst)
    return TUNING.SPIDERDEN_GROW_TIME[2] * (1 + math.random())
end

local function GetLargeGrowTime(inst)
    return TUNING.SPIDERDEN_GROW_TIME[3] * (1 + math.random())
end


local function onhammered(inst, worker)
	inst.components.sleepingbag:DoWakeUp()
    if inst.components.burnable ~= nil and inst.components.burnable:IsBurning() then
        inst.components.burnable:Extinguish()
    end
    inst:RemoveComponent("childspawner")
    inst.components.lootdropper:DropLoot()
    local fx = SpawnPrefab("collapse_big")
    fx.Transform:SetPosition(inst.Transform:GetWorldPosition())
    fx:SetMaterial("wood")
    inst:Remove()
end

local function SpawnQueen(inst, should_duplicate)
	inst.components.sleepingbag:DoWakeUp()
    local queen = SpawnPrefab("spiderqueen")
    local x, y, z = inst.Transform:GetWorldPosition()
    local rad = 1.25
    local angle = math.random(2 * PI)
    queen.Transform:SetPosition(x + rad * math.cos(angle), 0, z + rad * math.sin(angle))
    queen.sg:GoToState("birth")

    if not should_duplicate then
        inst:Remove()
    end
end

local function AttemptMakeQueen(inst)
    if inst.components.growable == nil then
        --failsafe in case we still got here after we are burning
        return
    end

    if inst.data.stage == nil or inst.data.stage ~= 3 then
        -- we got here directly (probably by loading), so reconfigure to the level 3 state.
        SetLarge(inst)
    end

    if not inst:IsNearPlayer(30) then
        inst.components.growable:StartGrowing(60 + math.random(60))
        return
    end

    local check_range = 60
    local cap = 4
    local x, y, z = inst.Transform:GetWorldPosition()
    local ents = TheSim:FindEntities(x, y, z, check_range, nil, nil, { "spiderden", "spiderqueen" })
    local num_dens = #ents

    inst.components.growable:SetStage(1)

    inst.AnimState:PlayAnimation("cocoon_large_burst")
    inst.AnimState:PushAnimation("cocoon_large_burst_pst")
    inst.AnimState:PushAnimation("cocoon_small", true)

    PlayLegBurstSound(inst)
    inst:DoTaskInTime(5 * FRAMES, PlayLegBurstSound)
    inst:DoTaskInTime(15 * FRAMES, PlayLegBurstSound)
    inst:DoTaskInTime(35 * FRAMES, SpawnQueen, num_dens < cap)

    inst.components.growable:StartGrowing(60)
    return true
end

local function onspawnspider(inst, spider)
    spider.sg:GoToState("taunt")
end

local function OnKilled(inst)
    inst.AnimState:PlayAnimation("cocoon_dead")
    if inst.components.childspawner ~= nil then
        inst.components.childspawner:ReleaseAllChildren()
    end
    RemovePhysicsColliders(inst)
	inst.components.sleepingbag:DoWakeUp()
    inst.SoundEmitter:KillSound("loop")

    inst.SoundEmitter:PlaySound("dontstarve/creatures/spider/spiderLair_destroy")
    inst.components.lootdropper:DropLoot(inst:GetPosition())
end

local function IsDefender(child)
    return child.prefab == "spider_warrior"
end

local function SpawnDefenders(inst, attacker)
    if not inst.components.health:IsDead() then
        inst.SoundEmitter:PlaySound("dontstarve/creatures/spider/spiderLair_hit")
        inst.AnimState:PlayAnimation(inst.anims.hit)
        inst.AnimState:PushAnimation(inst.anims.idle)
        if inst.components.childspawner ~= nil then
            local max_release_per_stage = { 2, 4, 6 }
            local num_to_release = math.min(max_release_per_stage[inst.data.stage] or 1, inst.components.childspawner.childreninside)
            local num_warriors = math.min(num_to_release, TUNING.SPIDERDEN_WARRIORS[inst.data.stage])
            num_to_release = math.floor(SpringCombatMod(num_to_release))
            num_warriors = math.floor(SpringCombatMod(num_warriors))
            num_warriors = num_warriors - inst.components.childspawner:CountChildrenOutside(IsDefender)
            for k = 1, num_to_release do
                inst.components.childspawner.childname = k <= num_warriors and "spider_warrior" or "spider"
                local spider = inst.components.childspawner:SpawnChild()
                if spider ~= nil and attacker ~= nil and spider.components.combat ~= nil then
                    spider.components.combat:SetTarget(attacker)
                    spider.components.combat:BlankOutAttacks(1.5 + math.random() * 2)
                end
            end
            inst.components.childspawner.childname = "spider"

            local emergencyspider = inst.components.childspawner:TrySpawnEmergencyChild()
            if emergencyspider ~= nil then
                emergencyspider.components.combat:SetTarget(attacker)
                emergencyspider.components.combat:BlankOutAttacks(1.5 + math.random() * 2)
            end
        end
    end
end

local function IsInvestigator(child)
    return child.components.knownlocations:GetLocation("investigate") ~= nil
end

local function SpawnInvestigators(inst, data)
    if not inst.components.health:IsDead() and MOBPVPMODE == "Enable" then
        inst.AnimState:PlayAnimation(inst.anims.hit)
        inst.AnimState:PushAnimation(inst.anims.idle)
        if inst.components.childspawner ~= nil then
            local max_release_per_stage = { 1, 2, 3 }
            local num_to_release = math.min(max_release_per_stage[inst.data.stage] or 1, inst.components.childspawner.childreninside)
            num_to_release = math.floor(SpringCombatMod(num_to_release))
            local num_investigators = inst.components.childspawner:CountChildrenOutside(IsInvestigator)
            num_to_release = num_to_release - num_investigators
            local targetpos = data ~= nil and data.target ~= nil and data.target:GetPosition() or nil
            for k = 1, num_to_release do
                local spider = inst.components.childspawner:SpawnChild()
                if spider ~= nil and targetpos ~= nil then
                    spider.components.knownlocations:RememberLocation("investigate", targetpos)
                end
            end
        end
    end
end

local function StartSpawning(inst)
    if inst.components.childspawner ~= nil and
        MOBPVPMODE == "Enable" and
        not TheWorld.state.iscaveday then
        inst.components.childspawner:StartSpawning()
    end
end

local function StopSpawning(inst)
    if inst.components.childspawner ~= nil then
        inst.components.childspawner:StopSpawning()
    end
end

local function OnIgnite(inst)
    if inst.components.childspawner ~= nil then
        SpawnDefenders(inst)
    end
	inst.components.sleepingbag:DoWakeUp()
    inst.SoundEmitter:KillSound("loop")
    DefaultBurnFn(inst)
end

local function OnFreeze(inst)
    --print(inst, "OnFreeze")
    inst.SoundEmitter:PlaySound("dontstarve/common/freezecreature")
    inst.AnimState:PlayAnimation(inst.anims.freeze, true)
    inst.AnimState:OverrideSymbol("swap_frozen", "frozen", "frozen")

    StopSpawning(inst)

    if inst.components.growable ~= nil then
        inst.components.growable:Pause()
    end
end

local function OnThaw(inst)
    --print(inst, "OnThaw")
    inst.AnimState:PlayAnimation(inst.anims.thaw, true)
    inst.SoundEmitter:PlaySound("dontstarve/common/freezethaw", "thawing")
    inst.AnimState:OverrideSymbol("swap_frozen", "frozen", "frozen")
end

local function OnUnFreeze(inst)
    --print(inst, "OnUnFreeze")
    inst.AnimState:PlayAnimation(inst.anims.idle, true)
    inst.SoundEmitter:KillSound("thawing")
    inst.AnimState:ClearOverrideSymbol("swap_frozen")

    StartSpawning(inst)

    if inst.components.growable ~= nil then
        inst.components.growable:Resume()
    end
end

local function OnEntityWake(inst)
    inst.SoundEmitter:PlaySound("dontstarve/creatures/spider/spidernest_LP", "loop")
end

local function OnEntitySleep(inst)
    inst.SoundEmitter:KillSound("loop")
end

local function OnIsCaveDay(inst, iscaveday)
if MOBPVPMODE == "Enable" then
    if iscaveday then
        StopSpawning(inst)
    else
        StartSpawning(inst)
    end
end
end

local function onhit(inst, worker)
    if not inst:HasTag("burnt") then
        if inst.components.childspawner ~= nil then
            SpawnDefenders(inst)
        end
        inst.AnimState:PlayAnimation("cocoon_small_hit")
        --inst.AnimState:PushAnimation("cocoon_small")
    end
end

local function OnInit(inst)
    inst:WatchWorldState("iscaveday", OnIsCaveDay)
    OnIsCaveDay(inst, TheWorld.state.iscaveday)
end

local function onbuilt(inst)
    inst.AnimState:PlayAnimation("place")
    --inst.AnimState:PushAnimation("idle", true)
	inst:WatchWorldState("iscaveday", OnIsCaveDay)
    OnIsCaveDay(inst, TheWorld.state.iscaveday)
end

local function OnStageAdvance(inst)
   inst.components.growable:DoGrowth()
   return true
end

local function OnUpgrade(inst)
   inst.AnimState:PlayAnimation(inst.anims.hit)
   inst.AnimState:PushAnimation(inst.anims.idle)
end

local growth_stages =
{
    { name = "small",   time = GetSmallGrowTime,    fn = SetSmall           },
    { name = "med",     time = GetMedGrowTime,      fn = SetMedium          },
    { name = "large",   time = GetLargeGrowTime,    fn = SetLarge           },
    { name = "queen",                               fn = AttemptMakeQueen   },
}

local function CanTarget(guy)
    return not guy.components.health:IsDead()
end

local function onsave(inst, data)
    if inst:HasTag("burnt") or (inst.components.burnable ~= nil and inst.components.burnable:IsBurning()) then
        data.burnt = true
    end
end

local function onload(inst, data)
    if data ~= nil and data.burnt then
        inst.components.burnable.onburnt(inst)
    end
end

local function onignite(inst)
    inst.components.sleepingbag:DoWakeUp()
end

local function onburntup(inst)
    inst.AnimState:PlayAnimation("burnt_rundown")
end

local function wakeuptest(inst, phase)
    --if phase ~= inst.sleep_phase then
        --inst.components.sleepingbag:DoWakeUp()
    --end
end

local function onwake(inst, sleeper, nostatechange)
    if inst.sleeptask ~= nil then
        inst.sleeptask:Cancel()
        inst.sleeptask = nil
    end

    inst:StopWatchingWorldState("phase", wakeuptest)
    sleeper:RemoveEventCallback("onignite", onignite, inst)

    if not nostatechange then
        if sleeper.sg:HasStateTag("house_sleep") then
            sleeper.sg.statemem.iswaking = true
        end
		--inst.SoundEmitter:PlaySound("dontstarve/common/pighouse_door")
        sleeper.sg:GoToState("taunt")
    end

    if inst.sleep_anim ~= nil then
        --inst.AnimState:PushAnimation("idle", true)
    end

    --inst.components.finiteuses:Use()
end

local function onsleeptick(inst, sleeper)
    local isstarving = sleeper.components.beaverness ~= nil and sleeper.components.beaverness:IsStarving()
	
	if not sleeper:HasTag("spiderwhisperer") then
		print ("Too bad he isn't a spider!")
		 inst.components.sleepingbag:DoWakeUp()
	end
	
    if sleeper.components.hunger ~= nil then
        sleeper.components.hunger:DoDelta(inst.hunger_tick, true, true)
        isstarving = sleeper.components.hunger:IsStarving()
    end

    if sleeper.components.sanity ~= nil and sleeper.components.sanity:GetPercentWithPenalty() < 1 then
        sleeper.components.sanity:DoDelta(TUNING.SLEEP_SANITY_PER_TICK, true)
    end

    if not isstarving and sleeper.components.health ~= nil then
        sleeper.components.health:DoDelta(TUNING.SLEEP_HEALTH_PER_TICK * 2, true, inst.prefab, true)
    end

    if sleeper.components.temperature ~= nil then
        if inst.is_cooling then
            if sleeper.components.temperature:GetCurrent() > TUNING.SLEEP_TARGET_TEMP_TENT then
                sleeper.components.temperature:SetTemperature(sleeper.components.temperature:GetCurrent() - TUNING.SLEEP_TEMP_PER_TICK)
            end
        elseif sleeper.components.temperature:GetCurrent() < TUNING.SLEEP_TARGET_TEMP_TENT then
            sleeper.components.temperature:SetTemperature(sleeper.components.temperature:GetCurrent() + TUNING.SLEEP_TEMP_PER_TICK)
        end
    end

    if isstarving then
        inst.components.sleepingbag:DoWakeUp()
    end
end

local function onsleep(inst, sleeper)
    --inst:WatchWorldState("phase", wakeuptest)
    sleeper:ListenForEvent("onignite", onignite, inst)
	--print ("someone wants to sleep in the den!")
	
	
    --if inst.sleep_anim ~= nil then
        --inst.AnimState:PlayAnimation(inst.sleep_anim, true)
    --end

    if inst.sleeptask ~= nil then
        inst.sleeptask:Cancel()
    end
	
	--if sleeper:HasTag("spider") then
	--inst.SoundEmitter:PlaySound("dontstarve/common/pighouse_door")
    inst.sleeptask = inst:DoPeriodicTask(TUNING.SLEEP_TICK_PERIOD, onsleeptick, nil, sleeper)
	
	--end
end

--local function OnIsDay(inst, isday)
    --if isday then
        --StopSpawning(inst)
    --elseif not inst:HasTag("burnt") then
        --if not TheWorld.state.iswinter then
            --inst.components.childspawner:ReleaseAllChildren()
        --end
        --StartSpawning(inst)
    --end
--end

--local function OnHaunt(inst)
    --if inst.components.childspawner == nil or
       --- not inst.components.childspawner:CanSpawn() or
        --math.random() > TUNING.HAUNT_CHANCE_HALF then
        --return false
    --end

    --local target = FindEntity(inst, 25, nil, { "character" }, { "merm", "playerghost", "INLIMBO" })
    --if target == nil then
        --return false
    --end

    --onhit(inst, target)
    --return true
--end

local function MakeSpiderDenFn(den_level)
	return function()
    local inst = CreateEntity()

    inst.entity:AddTransform()
        inst.entity:AddAnimState()
        inst.entity:AddGroundCreepEntity()
        inst.entity:AddSoundEmitter()
        inst.entity:AddMiniMapEntity()
        inst.entity:AddNetwork()

    MakeObstaclePhysics(inst, 0.5)

    inst.MiniMapEntity:SetIcon("spiderden.png")
	
	inst.hunger_tick = TUNING.SLEEP_HUNGER_PER_TICK
	
	MakeSnowCoveredPristine(inst)
	
	inst.AnimState:SetBank("spider_cocoon")
        inst.AnimState:SetBuild("spider_cocoon")
        inst.AnimState:PlayAnimation("cocoon_small", true)

        inst:AddTag("cavedweller")
        inst:AddTag("structure")
        inst:AddTag("chewable") -- by werebeaver
        inst:AddTag("spiderhouse") -- prevents other mobs from sleeping in here.
		inst:AddTag("shelter") -- this is to allow cooling during summer.
        inst:AddTag("spiderden")
		inst:AddTag("hostile")
        inst:AddTag("hive")

		inst.mobhouse = true
		
		--inst:SetPrefabName("spiderden")
    --MakeSnowCoveredPristine(inst)

    inst.entity:SetPristine()

    if not TheWorld.ismastersim then
        return inst
    end
	
	-------------------
       inst:AddComponent("health")
       inst.components.health:SetMaxHealth(200)

    -------------------
	
	inst:AddComponent("heater") 
	inst.components.heater.heat = 40
	
	inst:AddComponent("workable")
	inst:AddComponent("sleepingbag")
	--MakeHauntableWork(inst)
    inst.components.sleepingbag.onsleep = onsleep
    inst.components.sleepingbag.onwake = onwake
    --convert wetness delta to drying rate
    inst.components.sleepingbag.dryingrate = math.max(0, -TUNING.SLEEP_WETNESS_PER_TICK / TUNING.SLEEP_TICK_PERIOD)
	
	if MOBPVPMODE == "Enable" then
	inst:AddComponent("childspawner")
        inst.components.childspawner.childname = "spider"
        inst.components.childspawner:SetRegenPeriod(TUNING.SPIDERDEN_REGEN_TIME)
        inst.components.childspawner:SetSpawnPeriod(TUNING.SPIDERDEN_RELEASE_TIME)

        inst.components.childspawner.emergencychildname = "spider_warrior"
        inst.components.childspawner.emergencychildrenperplayer = 1

        inst.components.childspawner:SetSpawnedFn(onspawnspider)
        --inst.components.childspawner:SetMaxChildren(TUNING.SPIDERDEN_SPIDERS[stage])
        --inst.components.childspawner:ScheduleNextSpawn(0)
        inst:ListenForEvent("creepactivate", SpawnInvestigators)
	end	
	
	--inst.components.workable:SetWorkAction(ACTIONS.HAMMER)
    --inst.components.workable:SetWorkLeft(5)
    --inst.components.workable:SetOnFinishCallback(onhammered)
    --inst.components.workable:SetOnWorkCallback(onhit)
	
    inst:AddComponent("lootdropper")
    --inst.components.lootdropper:SetLoot(LOOT_DATA.SMALL)

    inst:AddComponent("hauntable")
    inst.components.hauntable:SetHauntValue(TUNING.HAUNT_SMALL)
    --inst.components.hauntable:SetOnHauntFn(OnHaunt)
	
	inst.data = {}
	
	 MakeMediumFreezableCharacter(inst)
        inst:ListenForEvent("freeze", OnFreeze)
        inst:ListenForEvent("onthaw", OnThaw)
        inst:ListenForEvent("unfreeze", OnUnFreeze)
    --inst:WatchWorldState("isday", OnIsDay)

		inst:DoTaskInTime(0, OnInit)
    --StartSpawning(inst)
	
	inst.hunger_tick = 0
	
		inst:AddComponent("combat")
        inst.components.combat:SetOnHit(SpawnDefenders)
        inst:ListenForEvent("death", OnKilled)
   --------------------

        inst:AddComponent("upgradeable")
        inst.components.upgradeable.upgradetype = UPGRADETYPES.SPIDER
        inst.components.upgradeable.onupgradefn = OnUpgrade
        inst.components.upgradeable.onstageadvancefn = OnStageAdvance

        ---------------------
        MakeMediumPropagator(inst)

        ---------------------
        inst:AddComponent("growable")
        inst.components.growable.springgrowth = true
        inst.components.growable.stages = growth_stages
        inst.components.growable:SetStage(den_level)
        inst.components.growable:StartGrowing()

        ---------------------
    --inst:ListenForEvent("onignite", onignite)
    --inst:ListenForEvent("burntup", onburntup)

    inst:AddComponent("inspectable")

    MakeSnowCovered(inst)
	
	inst.OnEntitySleep = OnEntitySleep
        inst.OnEntityWake = OnEntityWake

    return inst
	end
end

return Prefab("spidernest_p", MakeSpiderDenFn(1), assets, prefabs),
	Prefab("spidernest2_p", MakeSpiderDenFn(2), assets, prefabs),
	Prefab("spidernest3_p", MakeSpiderDenFn(3), assets, prefabs),
	MakePlacer("spidereggsack_placer", "spider_cocoon", "spider_cocoon", "cocoon_small")
