
local pl = world_storage.get("gm_windmills", "playerdata") or {}
local active_players = {}
local num_players = 0

pmb_gamemodes.add_gamemode_tags("gm_windmills", {
    inventory = true,
    replace_inventory = true,
    persistent_inside_inventory = false,
    replace_stats = true,
    crafting = true,
    damage = true,
    visible_to_mobs = true,
})

gm_windmills.until_next_state = 30

local formspec_get = {
    ["confirm_start"] = function(player)
        return
        "size[24.00,12.0]"..
        "no_prepend[]"..
        "listcolors[#66504f00;#554a6440]"..
        "bgcolor[#00000000]"..
        -- "background[1,1;12,10;pmb_inv_bg.png^[opacity:200]"..
        "style_type[image_button;bgimg=blank.png;bgimg_hovered=blank.png]"..
        "style_type[image_button;border=false]"..
        "image[0,0;8,8;gm_windmills_icon.png]"..
        "label[6.5,0.5;WARNING!]"..
        "label[6.5,1.0;THIS GAMEMODE IS MORE OR LESS UNTESTED.]"..
        "label[6.5,1.5;YOU MIGHT LOSE YOUR ITEMS OR GET TRAPPED SOMEWHERE!]"..
        "image_button[1.3,7.5;4,1;gm_windmills_button_start.png;".."start".."; Start]"
    end,
    ["confirm_leave"] = function(player)
        return
        "size[8.00,5.0]"..
        "no_prepend[]"..
        "listcolors[#66504f00;#554a6440]"..
        "bgcolor[#00000000]"..
        "style_type[image_button;bgimg=blank.png;bgimg_hovered=blank.png]"..
        "style_type[image_button;border=false]"..
        "label[2.0,1.5;Are you sure you want to leave?]"..
        "image_button[2,2;4,1;gm_windmills_button_start.png;".."leave".."; Confirm]"
    end,
    ["game_over"] = function(player)
        return
        "size[8.00,5.0]"..
        "no_prepend[]"..
        "listcolors[#66504f00;#554a6440]"..
        "bgcolor[#00000000]"..
        "style_type[image_button;bgimg=blank.png;bgimg_hovered=blank.png]"..
        "style_type[image_button;border=false]"..
        "label[2.0,0.5;Game Over!]"..
        "label[2,1.5;You failed to protect the windmills]"..
        "image_button[2,2;4,1;gm_windmills_button_start.png;".."leave".."; Leave game]"
    end,
    ["game_win"] = function(player)
        return
        "size[8.00,5.0]"..
        "no_prepend[]"..
        "listcolors[#66504f00;#554a6440]"..
        "bgcolor[#00000000]"..
        "style_type[image_button;bgimg=blank.png;bgimg_hovered=blank.png]"..
        "style_type[image_button;border=false]"..
        "label[2.0,0.5;Victory!]"..
        "label[2,1.5;You protected "..tostring(gm_windmills.windmill_count).." windmills!]"..
        "image_button[2,2;4,1;gm_windmills_button_start.png;".."leave".."; Leave game]"
    end,
}

local function clear_obj()
    for i, obj in ipairs(minetest.get_objects_in_area(gm_windmills.extra_minp, gm_windmills.extra_maxp)) do
    repeat
        if minetest.is_player(obj) then break end
        local ent = obj:get_luaentity()
        if ent and ent.name == "gm_windmills:windmill_head_ENTITY" then break end
        if ent._sound_burn then minetest.sound_stop(ent._sound_burn) end

        obj:remove()
    until true
    end
end

local function clear_fallen()
    local above_floor_maxp = vector.copy(gm_windmills.extra_maxp)
    above_floor_maxp.y = gm_windmills.minp.y + 4

    for i, obj in ipairs(minetest.get_objects_in_area(gm_windmills.extra_minp, above_floor_maxp)) do
    repeat
        if minetest.is_player(obj) then break end
        local ent = obj:get_luaentity()
        if not ent then break end

        if ent.name ~= "__builtin:item" then return end

        obj:remove()
    until true
    end
end

function gm_windmills.request_joinplayer(player)
    local name = player:get_player_name()
    minetest.show_formspec(name, "gm_windmills:confirm_start", formspec_get.confirm_start(player))
end
function gm_windmills.request_leaveplayer(player)
    local name = player:get_player_name()
    minetest.show_formspec(name, "gm_windmills:confirm_leave", formspec_get.confirm_leave(player))
end
function gm_windmills.to_end_of_game()
    clear_obj()
    clear_fallen()
    local win = gm_windmills.windmill_count > 0
    for i, player in ipairs(minetest.get_connected_players()) do
        local name = player:get_player_name()
        if win then
            minetest.show_formspec(name, "gm_windmills:end_of_game", formspec_get.game_win(player))
        else
            minetest.show_formspec(name, "gm_windmills:end_of_game", formspec_get.game_over(player))
        end
    end
end

function gm_windmills.move_player_to_outside_gamemode(player)
    local name = player:get_player_name()
    minetest.close_formspec(player:get_player_name(), "gm_windmills:end_of_game")

    pmb_gamemodes.set_gamemode(player, "survival")

    repeat
        if pl[name] and pl[name].outside_pos then
            player:set_pos(pl[name].outside_pos)
            pmb_statusfx.apply_status(player, "featherfalling", 3)
            break
        else
            minetest.chat_send_player(name, "Uhoh, there's a problem. We don't have your starting position, so when leaving\
            this gamemode, you're likely to end up somewhere unsafe. Good luck!")
            player:set_pos(vector.new(0, 40, 0))
            -- pmb_gamemodes.set_gamemode(player, "survival")
            pmb_statusfx.apply_status(player, "featherfalling", 10)
            break
        end
    until true

    pl[name] = nil
    active_players[player] = nil
    world_storage.set("gm_windmills", "playerdata", pl)
    world_storage.save("gm_windmills")
end

function gm_windmills.move_player_to_start(name)
    repeat
        local player = minetest.get_player_by_name(name)
        if not player then break end

        if pmb_gamemodes.get_gamemode(player) ~= "gm_windmills" then
            pl[name].outside_pos = player:get_pos()
            pmb_gamemodes.set_gamemode(player, "gm_windmills")
        end

        local to_pos = gm_windmills.game_origin
        to_pos.y = to_pos.y + 10
        player:set_pos(to_pos)
        pmb_statusfx.apply_status(player, "featherfalling", 3)
    until true
end

function gm_windmills.move_all_players_to_start()
    gm_windmills.generate_islands()
    gm_windmills.gamestate = "lobby"
    for name, def in pairs(pl) do
        gm_windmills.move_player_to_start(name)
    end
end

local function check_has_players()
    local has_players = false
    num_players = 0
    for player, info in pairs(active_players) do
        has_players = true
        num_players = num_players + 1
    end
    if not has_players then
        gm_windmills.gamestate = "waiting"
    end
end

function gm_windmills.on_windmill_destroyed(pos)
    gm_windmills.windmill_count = gm_windmills.windmill_count - 1
    if gm_windmills.windmill_count <= 0 then
        gm_windmills.gamestate = "end_of_game"
        gm_windmills.to_end_of_game()
    end
end

function gm_windmills.progress_game_state()
    if gm_windmills.gamestate == "waiting" then
        gm_windmills.gamestate = "generating"
        gm_windmills.windmill_count = 3
        minetest.delete_area(gm_windmills.minp, gm_windmills.maxp)
        gm_windmills.regenerate(math.random(30000), gm_windmills.progress_game_state)
        gm_windmills.until_next_state = 5
    elseif gm_windmills.gamestate == "generating" then
        gm_windmills.gamestate = "lobby"
        gm_windmills.move_all_players_to_start()
        gm_windmills.until_next_state = 10

        minetest.load_area(gm_windmills.extra_minp, gm_windmills.extra_maxp)
        clear_obj()
    elseif gm_windmills.gamestate == "lobby" then
        gm_windmills.gamestate = "active"
        gm_windmills.until_next_state = 300

        minetest.load_area(gm_windmills.extra_minp, gm_windmills.extra_maxp)
        clear_obj()

        minetest.sound_play("gm_windmills_start_horn", {
            gain = 1,
            pos = gm_windmills.game_origin,
            max_hear_distance = 100,
        })
    elseif gm_windmills.gamestate == "active" then
        gm_windmills.gamestate = "end_of_game"
        gm_windmills.to_end_of_game()
        gm_windmills.until_next_state = 30
    elseif gm_windmills.gamestate == "end_of_game" then
        gm_windmills.gamestate = "waiting"
        clear_obj()
        for player, i in pairs(active_players) do
            gm_windmills.move_player_to_outside_gamemode(player)
        end
        gm_windmills.until_next_state = 0
    end

    check_has_players()
end

pmb_util.formspec_actions.register_on_form_fields("gm_windmills:confirm_start", function(player, formname, fields)
    if fields and fields.start then
        minetest.close_formspec(player:get_player_name(), formname)

        local name = player:get_player_name()
        pl[name] = {outside_pos=player:get_pos()}
        active_players[player] = pl[name]
        world_storage.set("gm_windmills", "playerdata", pl)
        world_storage.save("gm_windmills")

        minetest.chat_send_player(name, "Joining...")
        if gm_windmills.gamestate == "waiting" then
            gm_windmills.progress_game_state()
        else
            gm_windmills.move_player_to_start(name)
        end
    end
end)

pmb_util.formspec_actions.register_on_form_fields("gm_windmills:confirm_leave", function(player, formname, fields)
    if fields and fields.leave then
        minetest.close_formspec(player:get_player_name(), formname)
        gm_windmills.move_player_to_outside_gamemode(player)
        check_has_players()
    end
end)

pmb_util.formspec_actions.register_on_form_fields("gm_windmills:end_of_game", function(player, formname, fields)
    if fields and fields.leave or fields.quit then
        minetest.close_formspec(player:get_player_name(), formname)
        gm_windmills.move_player_to_outside_gamemode(player)
        check_has_players()
    end
end)



minetest.register_on_joinplayer(function(player, last_login)

    if pmb_gamemodes.get_gamemode(player) == "gm_windmills" then
        gm_windmills.move_player_to_outside_gamemode(player)
        check_has_players()
        return
    end

    local name = player:get_player_name()
    if pl[name] then
        gm_windmills.move_player_to_outside_gamemode(player)
        check_has_players()
    end
end)

minetest.register_on_respawnplayer(function(player, reason)

    if pmb_gamemodes.get_gamemode(player) == "gm_windmills" then
        gm_windmills.move_player_to_outside_gamemode(player)
        check_has_players()
        return true
    end

    local name = player:get_player_name()
    if pl[name] then
        gm_windmills.move_player_to_outside_gamemode(player)
        check_has_players()
        return true
    end
end)

minetest.register_chatcommand("windmills", {
    params = "",
    description = "windmills [join, leave]",
    privs = { interact = 1 },
    func = function(name, param)
        local player = minetest.get_player_by_name(name)

        local splitparam = string.split(param, " ", true)

        if splitparam[1] == "join" and not pl[name] then
            gm_windmills.request_joinplayer(player)
            return true, ""
        elseif splitparam[1] == "leave" and pl[name] then
            gm_windmills.request_leaveplayer(player)
            return true, ""
        end

        return false, "no"
    end
})

local function sqdist(p, o)
    return (
        (o.x - p.x) ^ 2 +
        (o.y - p.y) ^ 2 +
        (o.z - p.z) ^ 2
    )
end

local function update_hud(remove)
    for player, info in pairs(active_players) do
        local name = player:get_player_name()
        if gm_windmills.gamestate == "active" then
            if not pl[name].hud then
                pl[name].hud = player:hud_add({
                    hud_elem_type = "text",
                    type = "text",
                    alignment = {x=0, y=-1},
                    position = {x=0.5, y=0.1},
                    name = "gm_windmills:time_left",
                    number = 0xFFEEAA,
                    text = tostring(gm_windmills.until_next_state),
                    z_index = -1000,
                    scale = {x = 100, y = 100},
                    offset = {x = 0, y = 0},
                    size = {x=6, y=6},
                    style = 5,
                })
            else
                player:hud_change(pl[name].hud, "text", tostring(gm_windmills.until_next_state))
            end
        elseif pl[name].hud then
            player:hud_remove(pl[name].hud)
            pl[name].hud = nil
        end
    end
end

local t = 1
local game_time = 0
local next_spawn = 3
local on_globalstep = function(dtime)
    if t > 0 then t = t - dtime return end t = 1
    if gm_windmills.gamestate ~= "waiting" and gm_windmills.until_next_state > 0 then
        gm_windmills.until_next_state = gm_windmills.until_next_state - 1
        if gm_windmills.until_next_state <= 0 then
            gm_windmills.progress_game_state()
        end
    end

    game_time = game_time + 1

    update_hud()

    if gm_windmills.gamestate == "active" then
        -- make sure there are still players, and also get the count
        check_has_players()

        -- spawn a wave
        if gm_windmills.gamestate == "active"
        and game_time > next_spawn
        and pmb_entity_api.has_mobs_in_radius(gm_windmills.game_origin, 100, {"gm_windmills:rockethead_ENTITY"}, 0, 2 * num_players) then
            next_spawn = game_time + 10
            local dir = vector.normalize(vector.new(
                math.random() - 0.5,
                math.random() - 0.5,
                math.random() - 0.5
            ))
            local dist = 70
            local pos = vector.add(gm_windmills.game_origin, vector.multiply(dir, dist))

            -- minetest.sound_play("gm_windmills_start_horn", {
            --     gain = 2,
            --     pos = pos,
            --     max_hear_distance = 200,
            --     pitch = 0.8
            -- })

            local f = 1 / 150

            -- spawn more if more players
            for i=1, math.random(1, 2 + game_time * f * ((num_players+1.2)*0.5)) do
                local p = vector.offset(pos,
                    (math.random()*2-0.5) * 10,
                    (math.random()*2-0.5) * 10,
                    (math.random()*2-0.5) * 10
                )
                local obj = minetest.add_entity(p, "gm_windmills:rockethead_ENTITY")
            end
        end

        clear_fallen()
    end
end
minetest.register_globalstep(on_globalstep)

pmb_gamemodes.register_on_start_gamemode("gm_windmills", function(player)
    local name = player:get_player_name()
    if not pl[name] then
        gm_windmills.move_player_to_outside_gamemode(player)
        check_has_players()
    end

    pmb_weather.override_player_weather(player, "windmills")

    local inv = player:get_inventory()
    inv:add_item("main", ItemStack("pmb_muskets:rifle_loaded"))
    inv:add_item("main", ItemStack("pmb_muskets:pistol_loaded"))
    inv:add_item("main", ItemStack("pmb_bow:bow"))
    inv:add_item("main", ItemStack("pmb_wood:scaffolding 80"))
end)

pmb_gamemodes.register_on_end_gamemode("gm_windmills", function(player)
    pmb_weather.unoverride_player_weather(player)

    local name = player:get_player_name()
    if pl[name] and pl[name].hud then
        player:hud_remove(pl[name].hud)
        pl[name].hud = nil
    end
end)

-- minetest.register_chatcommand("boom", {
--     params = "",
--     description = "",
--     privs = { interact = 1 },
--     func = function(name, param)
--         local player = minetest.get_player_by_name(name)

--         pmb_combat.explosions.explode(player:get_pos(), {
--             radius = 4
--         })

--         return false, "no"
--     end
-- })

pmb_weather.register_weather("windmills", {
    biomes = {},
    chance = 0,
    min_duration = 1200, -- sec
    max_duration = 1200,
    light_override = 1,
    on_enter_weather = function(player, meta, from_weather)
        pmb_weather.fix_timeofday(player, 1, true, 1)
        pmb_weather.request_sky(player, {
            base_color = "#ffffff",
            type = "regular",
            clouds = true,
            sky_color = {
                day_sky = pmb_weather.color(97, 181, 245),
                day_horizon = pmb_weather.color(144, 211, 246),
                dawn_sky = pmb_weather.color(180, 186, 250),
                dawn_horizon = pmb_weather.color(186, 193, 240),
                night_sky = pmb_weather.color(50, 41, 72),
                night_horizon = pmb_weather.color(50, 41, 71),
                indoors = pmb_weather.color(59, 59, 59),
                fog_sun_tint = pmb_weather.color(244, 125, 29),
                fog_moon_tint = pmb_weather.color(127, 153, 204),
                fog_tint_type = "custom",
            }
        })
        player:set_clouds({
            height = gm_windmills.game_origin.y - 90,
            thickness = 20,
            color = "#ffffffaa",
            speed = {x=6, z=4}
        })
        player:set_sun({
            visible = true,
            texture = "blank.png",
            sunrise_visible = false,
        })
        player:set_moon({
            visible = false,
        })
        player:set_stars({
            visible = false
        })
        pmb_weather.request_lighting(player, 0.33)
    end,
    on_leave_weather = function(player, meta, to_weather)
        player:set_sky()
        player:set_clouds()
        player:set_sun()
        player:set_moon()
        player:set_stars()
    end,
    on_step = function(player, dtime, meta)
    end,
    fallbacks = {
    },
})
