local waveassets =
{
	Asset( "ANIM", "anim/wave_ripple.zip" ),
}

local rogueassets = 
{
    Asset( "ANIM", "anim/wave_rogue.zip" ),
}


local function DebugDraw(inst)
	if not inst.draw then
		TheSim:SetDebugRenderEnabled(true)
		inst.draw = inst.entity:AddDebugRender()
	end
	if inst.draw then
		inst.draw:Flush()
		inst.draw:SetRenderLoop(true)
		inst.draw:SetZ(0.15)

		local px, py, pz = inst.Transform:GetWorldPosition()
		local cx, cy, cz = GetWorld().components.ocean:GetCurrentVec3()

		inst.draw:Line(px, pz, 50 * cx + px, 50 * cz + pz, 255, 0, 0, 255)
	end
end

local function wetanddamage(inst, other)
    --get wet and take damage
	if other and other.prefab == "wukong" and other.components.driver and other.components.driver.vehicle and other.components.driver.vehicle.prefab == "jindouyun" then
		return
	end
    if other and other.components.moisture then 
        local hitmoisturerate = 1.0
        if other.components.driver and other.components.driver.vehicle and other.components.driver.vehicle.components.drivable then
            hitmoisturerate = other.components.driver.vehicle.components.drivable:GetHitMoistureRate()
        end
        local waterproofMultiplier = 1 
        if other.components.inventory then 
            waterproofMultiplier = 1 - other.components.inventory:GetWaterproofness()
        end 
        other.components.moisture:DoDelta(inst.hitmoisture * hitmoisturerate * waterproofMultiplier)
    end 
    if other and other.components.driver and other.components.driver.vehicle then 
        local vehicle = other.components.driver.vehicle
        if vehicle.components.boathealth then
            vehicle.components.boathealth:DoDelta(inst.hitdamage, "wave")
        end
    end 
end

local function splash(inst)

    local splash = SpawnPrefab("splash_water")
    local pos = inst:GetPosition()
    splash.Transform:SetPosition(pos.x, pos.y, pos.z)

    inst:Remove()

end 

local function oncollidewave(inst, other)
	if other and other.prefab == "wukong" and other.components.driver and other.components.driver.vehicle and other.components.driver.vehicle.prefab == "jindouyun" then
		return
	end

    local boostThreshold = TUNING.WAVE_BOOST_ANGLE_THRESHOLD
    if other == GetPlayer() then-- and inst.sg:HasStateTag("idle") then
        local moving = GetPlayer().sg:HasStateTag("moving") 
        local playerAngle =  other.Transform:GetRotation()
        if playerAngle < 0 then playerAngle = playerAngle + 360 end 

        local waveAngle = inst.Transform:GetRotation()
        if waveAngle < 0 then waveAngle = waveAngle + 360 end 

        local angleDiff = math.abs(waveAngle - playerAngle)
        inst.SoundEmitter:PlaySound( "dontstarve_DLC002/common/wave_break")
        if angleDiff > 360 then angleDiff = angleDiff - 360 end 
        
        if angleDiff < boostThreshold and moving then
            --Do boost
            local rogueboost
            local player = GetPlayer()
            if other == player then
                if player.components.driver.vehicle and player.components.driver.vehicle.prefab == "surfboard" then
                    rogueboost = TUNING.SURFBOARD_ROGUEBOOST
                end
            end
            other:PushEvent("boostbywave", {position = inst.Transform:GetWorldPosition(), velocity = inst.Physics:GetVelocity(), boost = rogueboost})
            inst.SoundEmitter:PlaySound( "dontstarve_DLC002/common/wave_boost")
        else
            wetanddamage(inst, other)
        end 

        splash(inst)
    elseif other and other.components.waveobstacle then
        other.components.waveobstacle:OnCollide(inst)
        wetanddamage(inst, other)
        splash(inst)
    end
end 


local function oncolliderogue(inst, other)
    -- check for surfboard, which actually just boosts
	if other and other.prefab == "wukong" and other.components.driver and other.components.driver.vehicle and other.components.driver.vehicle.prefab == "jindouyun" then
		return
	end
    local player = GetPlayer()
    if other == player then
        if player.components.driver.vehicle and player.components.driver.vehicle.prefab == "surfboard" then
            oncollidewave(inst, other)
            return
        else
            wetanddamage(inst, other)
            splash(inst)
            return 
        end
    end

    if other and other.components.waveobstacle then
        other.components.waveobstacle:OnCollide(inst)
        wetanddamage(inst, other)
        splash(inst)
    end
   
end 

local function CheckGround(inst, dt)
    --Check if I'm about to hit land 
    local x, y, z = inst.Transform:GetWorldPosition()
    local vx, vy, vz = inst.Physics:GetVelocity()
    

    local checkx = x + vx 
    local checky = y
    local checkz = z + vz 

    local ground = GetWorld()
    local tile = GROUND.GRASS
    if ground and ground.Map then
        tile = ground.Map:GetTileAtPoint(checkx, checky, checkz)
    end

    if not ground.Map:IsWater(tile) then 
        splash(inst)
    end
end 

local function onsave(inst, data)
    if inst and data then
        data.speed = inst.Physics:GetMotorSpeed()
        data.angle = inst.Transform:GetRotation()
        if inst.sg and inst.sg.currentstate and inst.sg.currentstate.name then
            data.state = inst.sg.currentstate.name
        end
    end
end

local function onload(inst, data)
    if inst and data then
        inst.Transform:SetRotation(data.angle or 0)
        inst.Physics:SetMotorVel(data.speed or 0, 0, 0)
        if inst.sg and data.state then
            inst.sg:GoToState(data.state)
        end
    end
end

local function activate_collision(inst)
    inst.Physics:SetCollides(false) --Still will get collision callback, just not dynamic collisions.
    inst.Physics:SetCollisionGroup(COLLISION.WAVES)
    inst.Physics:CollidesWith(COLLISION.CHARACTERS)
    inst.Physics:CollidesWith(COLLISION.OBSTACLES)
end

local function joinherd(inst)
    local x, y, z = inst.Transform:GetWorldPosition()
    local herd = nil
    local ents = TheSim:FindEntities(x, y, z, 20, {"rogueherd"})
    if ents then
        for k, v in pairs(ents) do
            if GetTableSize(v.waves) < 4 then
                --print("join herd")
                herd = v
                break
            end
        end
    end
    if herd == nil then
        --no herd? make one
        --print("make herd")
        herd = SpawnPrefab("rogue_wave_herd")
    end
    if herd then
        herd.waves[inst.GUID] = inst
        inst.herd = herd
        inst.herd:UpdatePos()
    end
end

local function onRemove(inst)
    if inst and inst.herd and inst.herd.waves[inst.GUID] then
        --print("leave herd")
        inst.herd.waves[inst.GUID] = nil
        if GetTableSize(inst.herd.waves) == 0 then
            --print("remove herd")
            inst.herd:Remove()
        else
            inst.herd:UpdatePos()
        end
        inst.herd = nil
    end

    if inst and inst.soundloop then
        inst.SoundEmitter:KillSound(inst.soundloop)
    end
end

local function onSleep(inst)
    inst:Remove()
end

local function common(Sim)
	local inst = CreateEntity()
	local trans = inst.entity:AddTransform()
    inst.entity:AddSoundEmitter()
    trans:SetFourFaced()

    inst.entity:AddPhysics()

	inst:AddTag( "FX" )
    inst.Physics:SetSphere(1)

    inst.Physics:ClearCollisionMask()
    inst.checkgroundtask = inst:DoPeriodicTask(0.5, CheckGround)

    inst.OnSave = onsave
    inst.OnLoad = onload
    inst.OnEntitySleep = onSleep    
    inst.done = false

    return inst
end


local function ripple(Sim)
    local inst = common(Sim)
    inst.hitdamage = -TUNING.WAVE_HIT_DAMAGE
    inst.hitmoisture = TUNING.WAVE_HIT_MOISTURE
    inst.Physics:SetCollisionCallback(oncollidewave)
    local anim = inst.entity:AddAnimState()
    anim:SetBuild( "wave_ripple" )
    anim:SetBank( "wave_ripple" )
    inst:SetStateGraph("SGwave")
    inst.soundrise = "small"
    inst.activate_collision = activate_collision
    return inst
end 

local function rogue(Sim)
    local inst = common(Sim)
    inst.hitdamage = -TUNING.ROGUEWAVE_HIT_DAMAGE
    inst.hitmoisture = TUNING.ROGUEWAVE_HIT_MOISTURE
    inst.Physics:SetCollisionCallback(oncolliderogue)
    local anim = inst.entity:AddAnimState()
    anim:SetBuild("wave_rogue" )
    anim:SetBank( "wave_rogue" )
    inst:SetStateGraph("SGwave")
    inst.idle_time = 1
    inst.soundrise = "large"
    inst.soundloop = "large_LP"
    inst.JoinHerd = joinherd
    inst.activate_collision = activate_collision
    inst:DoTaskInTime(30*FRAMES, joinherd)
    inst:ListenForEvent("onremove", onRemove)
    return inst
end

local function updatepos(inst)
    local hx, hy, hz = 0, 0, 0
    local waves = 0
    for k,v in pairs(inst.waves) do
        if v then
            local wx, wy, wz = v.Transform:GetWorldPosition()
            hx, hy, hz = hx + wx, hy + wy, hz + wz
            waves = waves + 1
        end
    end
    --print("RogueHerd", waves)
    if waves > 0 then
        inst.Transform:SetPosition(hx / waves, hy / waves, hz / waves)
    end
    if waves >= 4 then
        --print("tidal_wave")
        if not inst.SoundEmitter:PlayingSound("tidal_wave") then
            inst.SoundEmitter:PlaySound("dontstarve_DLC002/common/rogue_waves/tidal_wave", "tidal_wave")
        end

    elseif inst.SoundEmitter:PlayingSound("tidal_wave") then
        inst.SoundEmitter:KillSound("tidal_wave")
    end
end

local function herd(Sim)
    local inst = CreateEntity()
    inst.entity:AddTransform()
    inst.entity:AddSoundEmitter()

    --[[inst.entity:AddAnimState()
    local minimap = inst.entity:AddMiniMapEntity()
    minimap:SetIcon( "accomplishment_shrine.png" )
    inst:AddTag("irreplaceable")
    inst.AnimState:SetBank("eyebone")
    inst.AnimState:SetBuild("chester_eyebone_build")
    inst.AnimState:PlayAnimation("idle_loop", true)]]
    inst:AddTag("NOCLICK")
    inst:AddTag("rogueherd")
    inst.waves = {}
    inst.UpdatePos = updatepos
    return inst
end

return Prefab( "common/fx/wave_ripple", ripple, waveassets ), 
       Prefab( "common/fx/rogue_wave", rogue, rogueassets ),
       Prefab( "common/fx/rogue_wave_herd", herd )
