require("stategraphs/commonstates")

local actionhandlers = 
{
}

local events=
{
    CommonHandlers.OnLocomote(true, true),
    CommonHandlers.OnSleep(),
    CommonHandlers.OnFreeze(),
    CommonHandlers.OnAttack(),
    CommonHandlers.OnAttacked(),
    CommonHandlers.OnDeath(),	
	EventHandler("locomote", function(inst) 
        if not inst.sg:HasStateTag("busy") then            
            local is_moving = inst.sg:HasStateTag("moving")
            local wants_to_move = inst.components.locomotor:WantsToMoveForward()
			local is_running = inst.sg:HasStateTag("running") or inst.sg:HasStateTag("runningattack")
            if not inst.sg:HasStateTag("attack") and is_moving ~= wants_to_move then
                if wants_to_move then
					if math.random() < 0.25 and inst.components.combat.target then
						inst.sg:GoToState("run_start")
					else
						inst.sg:GoToState("walk_start")
					end				
                else
					if is_running then
						inst.sg:GoToState("run_stop")
					else
						inst.sg:GoToState("walk_stop")
					end
                end
            end
        end		
	end),
	
	EventHandler("doattack", function(inst, data) 
		local is_running = inst.sg:HasStateTag("running") or inst.sg:HasStateTag("runningattack")
		if not inst.components.health:IsDead() and not inst.sg:HasStateTag("busy") and data and data.target then 
			if is_running then
				inst.sg:GoToState("runningattack")
			else
				local chance = math.random(1,5)
				if chance == 1 then
					inst.sg:GoToState("pound_start") 
				elseif chance == 2 then
					inst.sg:GoToState("lightning")
				else
					inst.sg:GoToState("normalattack")
				end
			end
		end
    end),	
}

local states=
{
     State
	 {     
		name = "idle",
        tags = {"idle", "canrotate"},
		
        onenter = function(inst, playanim)
            inst.Physics:Stop()
            inst.SoundEmitter:KillSound("charge")
            if playanim then
                inst.AnimState:PlayAnimation(playanim)
                inst.AnimState:PushAnimation("idle", true)
            else
                inst.AnimState:PlayAnimation("idle", true)
            end
        end,
        
        timeline = 
        {
		    TimeEvent(21*FRAMES, function(inst) inst.SoundEmitter:PlaySound(inst.soundpath .. "idle") end),
        },
        
        events=
        {
            EventHandler("animover", function(inst) inst.sg:GoToState("idle") end),
        },
    },

    State
	{  
		name = "run_start",
		tags = {"moving", "running", "busy", "atk_pre", "canrotate"},
            
		onenter = function(inst)
			inst.Physics:Stop()
			inst.SoundEmitter:PlaySound(inst.soundpath .. "pawground")
			inst.AnimState:PlayAnimation("atk_pre")
			inst.AnimState:PlayAnimation("paw_loop", true)
			if inst.components.combat.target then	
				local pt = Point(inst.components.combat.target.Transform:GetWorldPosition())
				inst:FacePoint(pt)
			end
			inst.sg:SetTimeout(1)
		end,
            
		ontimeout= function(inst)
			inst.sg:GoToState("run")
			inst:PushEvent("attackstart")
		end,
            
		timeline=
		{
		    TimeEvent(1*FRAMES, function(inst) inst.SoundEmitter:PlaySound(inst.effortsound) end),
		    TimeEvent(12*FRAMES, function(inst) inst.SoundEmitter:PlaySound(inst.soundpath .. "pawground") end),
            TimeEvent(15*FRAMES, function(inst) inst.sg:RemoveStateTag("canrotate") end),
		    TimeEvent(20*FRAMES, function(inst) inst.SoundEmitter:PlaySound(inst.effortsound) end),
		    TimeEvent(30*FRAMES, function(inst) inst.SoundEmitter:PlaySound(inst.soundpath .. "pawground") end),
		    TimeEvent(35*FRAMES,  function(inst) inst.SoundEmitter:PlaySound(inst.effortsound) end),
		},        

		onexit = function(inst)
			inst.SoundEmitter:PlaySound(inst.soundpath .. "charge_LP","charge")
		end,
	},

    State
	{  
		name = "run",
		tags = {"moving", "running"},
            
		onenter = function(inst) 
			inst.components.locomotor:RunForward()
			inst.AnimState:PlayAnimation("atk")
			if inst.components.combat.target then	
				local pt = Point(inst.components.combat.target.Transform:GetWorldPosition())
				inst:FacePoint(pt)
			end	
		end,
            
		timeline=
		{
			TimeEvent(5*FRAMES, function(inst) 
				inst.SoundEmitter:PlaySound(inst.effortsound)
				SpawnPrefab("ground_chunks_breaking").Transform:SetPosition(inst.Transform:GetWorldPosition()) 
				local player = GetClosestInstWithTag("player", inst, 40)
				if player then
					player.components.playercontroller:ShakeCamera(inst, "VERTICAL", 0.5, 0.03, 2, 40)
				end	
			end),				
		},
            
		events=
		{   
			EventHandler("animover", function(inst) 
				local targetPos =  inst.components.knownlocations:GetLocation("targetpos")
				local myPos = Vector3(inst.Transform:GetWorldPosition())
				local v1 = Vector3(inst.Physics:GetVelocity())
				if (targetPos and distsq(targetPos, myPos) > 30*30) then
					inst.sg:GoToState("run_stop") 
				else
					inst.sg:GoToState("run") 
				end
			end),        
		},
	},
    
    State
	{  
		name = "run_stop",
		tags = {"canrotate", "idle"},
            
		onenter = function(inst) 
			inst.SoundEmitter:KillSound("charge")
			inst.components.locomotor:Stop()
			inst.AnimState:PlayAnimation("atk_pst")
			inst.SoundEmitter:PlaySound(inst.effortsound)
		end,
            
		events=
		{   
			EventHandler("animover", function(inst) inst.sg:GoToState("walk_start") end),        
		},
	},    

	State
	{
		name = "taunt",
		tags = {"busy"},
        
        onenter = function(inst)
            inst.Physics:Stop()
            inst.AnimState:PlayAnimation("taunt")
            inst.SoundEmitter:PlaySound(inst.soundpath .. "voice")
        end,
        
        timeline = 
        {
		    TimeEvent(10*FRAMES, function(inst) inst.SoundEmitter:PlaySound("epicworld/epicworld/Fortress_coming_1") end),
        },
        
        events=
        {
            EventHandler("animover", function(inst) inst.sg:GoToState("idle") end),
        },
    },

    State
	{  
		name = "runningattack",     
		tags = {"runningattack"},
            
		onenter = function(inst)
			inst.SoundEmitter:KillSound("charge")
			--inst.components.combat:StartAttack()
			inst.components.locomotor:StopMoving()
			inst.SoundEmitter:PlaySound(inst.effortsound)
			inst.AnimState:PlayAnimation("atk_pst")
		end,
            
		timeline =
		{
			TimeEvent(1*FRAMES, function(inst) inst.components.combat:DoAttack() end),
		},
            
		events =
		{
			EventHandler("animqueueover", function(inst) inst.sg:GoToState("idle") end),
		},
	},
	
	State
	{  
		name = "normalattack",     
		tags = {"attack", "busy"},
            
		onenter = function(inst)
			inst.SoundEmitter:KillSound("charge")
			inst.components.combat:StartAttack()
			inst.components.locomotor:StopMoving()
			inst.SoundEmitter:PlaySound(inst.effortsound)
			inst.AnimState:PlayAnimation("gore")
		end,
            
		timeline =
		{
			TimeEvent(9*FRAMES, function(inst) inst.components.combat:DoAttack() end),
		},
            
		events =
		{
			EventHandler("animqueueover", function(inst) inst.sg:GoToState("normalattack2") end),
		},
	},
	
	State
	{  
		name = "normalattack2",     
		tags = {"attack", "busy"},
            
		onenter = function(inst)
			inst.SoundEmitter:KillSound("charge")
			inst.components.combat:StartAttack()
			inst.components.locomotor:StopMoving()
			inst.SoundEmitter:PlaySound(inst.effortsound)
			inst.AnimState:PlayAnimation("gore")
		end,
            
		timeline =
		{
			TimeEvent(9*FRAMES, function(inst) inst.components.combat:DoAttack() end),
		},
            
		events =
		{
			EventHandler("animqueueover", function(inst) inst.sg:GoToState("idle") end),
		},
	},
	
	State
	{
		name = "pound_start",
		tags = {"attack", "busy", "pound"},
        
        onenter = function(inst)
            inst.Physics:Stop()
            inst.AnimState:PlayAnimation("taunt")
            inst.SoundEmitter:PlaySound(inst.soundpath .. "voice")
			inst.pound = 0
        end,
        
        timeline = 
        {
		    TimeEvent(8*FRAMES,  function(inst) inst.SoundEmitter:PlaySound("epicworld/epicworld/Fortress_coming_1") end),
        },
        
        events=
        {
            EventHandler("animover", function(inst) inst.sg:GoToState("pound") end),
        },
    },
	
	State
	{  
		name = "pound",     
		tags = {"attack", "busy", "pound"},
            
		onenter = function(inst)
			inst.SoundEmitter:KillSound("charge")
			inst.components.combat:StartAttack()
			inst.components.locomotor:StopMoving()
			inst.SoundEmitter:PlaySound(inst.effortsound)
			inst.AnimState:PlayAnimation("walk_loop")
			inst.pound = inst.pound + 1
		end,
            
		timeline =
		{
			TimeEvent(7*FRAMES, function(inst) inst.SoundEmitter:PlaySound(inst.soundpath .. "bounce") end),	
            TimeEvent(20*FRAMES, function(inst)
		        inst.SoundEmitter:PlaySound(inst.effortsound)
                if (IsDLCEnabled(CAPY_DLC) or IsDLCEnabled(REIGN_OF_GIANTS)) then
					inst.components.groundpounder:GroundPound()
					inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/bearger/groundpound")
				else
					inst.components.combat:DoAreaAttack(inst, 7, nil, nil, nil, { "INLIMBO", "notarget", "invisible", "noattack", "flight", "playerghost", "shadow", "chess"})
				end
				local player = GetClosestInstWithTag("player", inst, 40)
				if player then
					player.components.playercontroller:ShakeCamera(inst, "VERTICAL", 0.5, 0.03, 2, 40)
				end
            end),		
		},
            
		events =
		{
			EventHandler("animqueueover", function(inst) 
				if inst.pound < 4 then
					inst.sg:GoToState("pound") 
				else
					inst.sg:GoToState("idle")
				end
			end),
		},
	},
		
	State
	{
		name = "lightning",
		tags = {"attack", "busy", "lightning"},
        
        onenter = function(inst)
            inst.Physics:Stop()
            inst.AnimState:PlayAnimation("taunt")
            inst.SoundEmitter:PlaySound(inst.soundpath .. "voice")
			inst.pound = 0
        end,
        
        timeline = 
        {
		    TimeEvent(8*FRAMES, function(inst) 
				inst.SoundEmitter:PlaySound("epicworld/epicworld/Fortress_coming_1")
				inst:DoTaskInTime(1, function(inst) local pt = Point(inst.Transform:GetWorldPosition()) GetSeasonManager():DoLightningStrike(pt) end)
				inst:DoTaskInTime(2, function(inst) local pt = Point(inst.Transform:GetWorldPosition()) GetSeasonManager():DoLightningStrike(pt) end)
				inst:DoTaskInTime(3, function(inst) local pt = Point(inst.Transform:GetWorldPosition()) GetSeasonManager():DoLightningStrike(pt) end)
				inst:DoTaskInTime(4, function(inst) local pt = Point(inst.Transform:GetWorldPosition()) GetSeasonManager():DoLightningStrike(pt) end)
				inst:DoTaskInTime(5, function(inst) local pt = Point(inst.Transform:GetWorldPosition()) GetSeasonManager():DoLightningStrike(pt) end)
			end),
        },
        
        events=
        {
            EventHandler("animover", function(inst) inst.sg:GoToState("idle") end),
        },
    },
}

CommonStates.AddWalkStates(states,
{
    starttimeline = 
    {
	    TimeEvent(0*FRAMES, function(inst) inst.Physics:Stop() end ),
    },
	walktimeline = {
		    TimeEvent(0*FRAMES, function(inst) inst.Physics:Stop() end ),
            TimeEvent(7*FRAMES, function(inst) 
                inst.SoundEmitter:PlaySound(inst.soundpath .. "bounce")
                inst.components.locomotor:WalkForward()
            end ),
            TimeEvent(20*FRAMES, function(inst)
		        inst.SoundEmitter:PlaySound(inst.effortsound)
                inst.SoundEmitter:PlaySound(inst.soundpath .. "land")
				local player = GetClosestInstWithTag("player", inst, 40)
				if player then
					player.components.playercontroller:ShakeCamera(inst, "VERTICAL", 0.5, 0.03, 2, 40)
				end
                inst.Physics:Stop()
            end ),
	},
}, nil,true)

CommonStates.AddSleepStates(states,
{
    starttimeline = 
	{
		TimeEvent(11*FRAMES, function(inst) inst.SoundEmitter:PlaySound(inst.soundpath .. "liedown") end ),
    },
    
	sleeptimeline = 
	{
        TimeEvent(18*FRAMES, function(inst) inst.SoundEmitter:PlaySound(inst.soundpath .. "sleep") end),
	},
})

CommonStates.AddCombatStates(states,
{
    attacktimeline = 
    {
        TimeEvent(15*FRAMES, function(inst) inst.SoundEmitter:PlaySound(inst.soundpath .. "explo") end),
        TimeEvent(17*FRAMES, function(inst)
                                inst.components.combat:DoAttack()
                             end),
    },
    hittimeline = 
    {
        TimeEvent(0*FRAMES, function(inst) inst.SoundEmitter:PlaySound(inst.soundpath .. "hurt") end),
    },
    deathtimeline = 
    {
        TimeEvent(0*FRAMES, function(inst) inst.SoundEmitter:PlaySound(inst.soundpath .. "explo") end),
    },
})

CommonStates.AddFrozenStates(states)

    
return StateGraph("fortress", states, events, "idle", actionhandlers)