local S = core.get_translator(core.get_current_modname());
local modpath = core.get_modpath(core.get_current_modname());
local F = core.formspec_escape;

local yaw_map = { [0]=math.pi, [1]=math.pi/2, [2]=0, [3]=-math.pi/2};
local gate_yaw_map = { [0]=0, [1]=-math.pi/2, [2]=math.pi, [3]=math.pi/2};
local achvnt = nil;

local storage = core.get_mod_storage();
local is_player_standing = {};

warp_portal = {
    storage_id = "get_storage_all_keys",
};

dofile(modpath.."/utils/game.lua");
dofile(modpath.."/utils/node_map.lua");
dofile(modpath.."/utils/formspec.lua");

local anim_close = { x=0, y=17 };
local anim_open = { x=17, y=35 };
local anim_blend = warp_portal.var_impact(not storage.get_keys, 0, 2);

-- SCROLL
core.register_entity("warp_portal:scroll", {
    initial_properties = {
        visual = "mesh", visual_size = { x=10, y=10, z=10 },
        textures = { "default_paper.png", "default_wood.png" },
        mesh = "scroll.b3d",
        pointable = false,
        physical = false,
    },
    on_activate = function(self, staticdata, dtime_s)
        self.object:set_animation(anim_close, 30, anim_blend, false);
    end,
});

-- GATE
core.register_entity("warp_portal:gate", {
    initial_properties = {
        visual = "mesh", visual_size = { x=10, y=10, z=10 },
        textures = { "warp_gate_texture.png" },
        glow = 2, use_texture_alpha = true,
        mesh = "portal_gate.obj",
        pointable = false,
        physical = false,
    },
});

-- HIDEN PART NODE
core.register_node("warp_portal:hidden_part", {
    description = "Hidden Part",
    paramtype = "light", paramtype2 = "facedir",
    drawtype = "airlike",
    groups = { not_in_creative_inventory=1, },
    sunlight_propagates = true,
    walkable = false, pointable = false,
    diggable = false, buildable_to = false,
    floodable = false, drop = "",
    on_blast = function() end,
});

-- LIGHT SOURCE
core.register_node("warp_portal:light", {
    description = "Light Part",
    paramtype = "light", paramtype2 = "facedir",
    drawtype = "airlike",
    groups = { not_in_creative_inventory=1, },
    sunlight_propagates = true,
    light_source = 10,
    walkable = false, pointable = false,
    diggable = false, buildable_to = false,
    floodable = false, drop = "",
    on_blast = function() end,
});

-- SOUNDS
local function get_sounds()
    if core.get_modpath("mcl_sounds") then
        return mcl_sounds.node_sound_stone_defaults();
        
    elseif core.get_modpath("default") then
        return default.node_sound_stone_defaults();
        
    else
        return {};
    end;
end;

local portal_box = {
    type = "fixed",
    fixed = {
        { -1.0, -0.5, -0.3,  1.0, 0.0, 0.5 },
        { -0.3,  0.0,  0.35, 0.3, 1.0, 0.5 },
        { -1.0,  0.0, -0.3, -0.5, 2.0, 0.5 },
        {  1.0,  0.0, -0.3,  0.5, 2.0, 0.5 },
        { -1.0,  2.0, -0.3,  1.0, 2.5, 0.5 },
    },
};

local portal_icon = warp_portal.impact(
    "warp_portal.png", {
        ["minetest"]   = "mts_portal.png",
        ["mineclonia"] = "mcl_portal.png",
        ["mineclone2"] = "mcl_portal.png",
        ["asuna"]      = "mts_portal.png",
        ["repixture"]  = "rep_portal.png",
    }
);

-- PORTAL
core.register_node("warp_portal:portal", {
    description = S("Warp Portal"),
    _doc_items_longdesc = S("Warp Portal - allows you to teleport between other warp portals."),
    _doc_items_usagehelp = S(""),
    
    is_ground_content = false,
    inventory_image = portal_icon,
    tiles = warp_portal.impact(
        nil, {
            ["minetest"] = {
                "default_stone_brick.png", "default_stone_block.png",
                "default_obsidian.png",    "default_mese_block.png",
                "default_stone_block.png", "default_wood.png",
            },
            ["mineclonia"] = {
                "default_stone_brick.png",    "mcl_blackstone_basalt_side_polished.png",
                "default_obsidian.png",       "redstone_redstone_block.png",
                "mcl_deepslate_polished.png", "default_wood.png",
            },
            ["mineclone2"] = {
                "default_stone_brick.png",    "mcl_blackstone_basalt_side_polished.png",
                "default_obsidian.png",       "redstone_redstone_block.png",
                "mcl_polished_deepslate.png", "default_wood.png",
            },
            ["asuna"] = {
                "default_stone_brick.png", "default_stone_block.png",
                "default_obsidian.png",    "default_mese_block.png",
                "default_stone_block.png", "default_wood.png",
            },
            ["repixture"] = {
                "default_reinforced_cobbles.png",     "default_stone.png",
                "default_block_wrought_iron.png",     "lumien_block.png",
                "partialblocks_block_steel_slab.png", "default_wood.png",
            },
        }
    ),
    drawtype = "mesh",
    mesh = "portal.obj",
    drop = "warp_portal:portal",
    collision_box = portal_box,
    selection_box = portal_box,
    stack_max = 1,
    paramtype = "light",
    paramtype2 = "facedir",
    
    on_place = function(itemstack, user, pointed_thing)
        local pos = pointed_thing.above;
        local param2 = core.dir_to_facedir(user:get_look_dir()) % 4;
        
        local nodes = warp_portal.get_node_map(pos, {
            vector.new(-1, 2, 0), vector.new(0, 2, 0), vector.new(1, 2, 0),
            vector.new(-1, 1, 0), vector.new(0, 1, 0), vector.new(1, 1, 0),
            vector.new(-1, 0, 0), vector.new(0, 0, 0), vector.new(1, 0, 0),
        });
        
        for i, node in pairs(nodes) do
            if (not node.name == "air") and (not node.node.buildable_to == true) then
                return itemstack;
            end;
        end;
        
        core.set_node(pos, {name="warp_portal:portal", param2=param2});
        warp_portal.set_node_map(pos, "warp_portal:hidden_part", {
            vector.new(-1, 2, 0), vector.new(0, 2, 0), vector.new(1, 2, 0),
            vector.new(-1, 1, 0), vector.new(0, 1, 0), vector.new(1, 1, 0),
            vector.new(-1, 0, 0),                      vector.new(1, 0, 0),
        });
        
        warp_portal.set_name_formspec(pos, user);
        
        if not core.is_creative_enabled(user:get_player_name()) then
            itemstack:take_item();
        end;
        
        return itemstack;
    end,
    on_destruct = function(pos)
        local meta = core.get_meta(pos);
        local name = meta:get_string("infotext");
        
        print(core.get_node(pos).name)
        
        warp_portal.storage_remove(storage, name);
        
        local nodes = warp_portal.set_node_map(pos, "air", {
            vector.new(-1, 2, 0), vector.new(0, 2, 0), vector.new(1, 2, 0),
            vector.new(-1, 1, 0), vector.new(0, 1, 0), vector.new(1, 1, 0),
            vector.new(-1, 0, 0), vector.new(0, 0, 0), vector.new(1, 0, 0),
        }, true);
            
        for i, obj in ipairs(core.get_objects_inside_radius(pos, 1)) do
            if (obj and (obj:get_luaentity().name == "warp_portal:scroll")) or
                (obj and (obj:get_luaentity().name == "warp_portal:gate")) then
                    obj:remove();
            end;
        end;
        
        core.sound_play("turn_off", { pos=pos });
    end,
    
    groups = warp_portal.impact(
        { cracky=1, level=2 }, {
            ["minetest"] = { cracky=1, level=2 },
            ["mineclonia"] = { pickaxey=4, material_stone=1, unmovable_by_piston=1 },
            ["mineclone2"] = { pickaxey=4, material_stone=1, unmovable_by_piston=1 },
            ["asuna"] = { cracky=1, level=2 },
            ["repixture"] = { cracky=1, stone=1, pathfinder_hard=1 },
        }
    ),
    sounds = get_sounds(),
    
    _mcl_blast_resistance = 1000,
    _mcl_hardness = 32,
    pointable = true,
});

-- CHECK STATUS
local function get_status(pos)
    local objs = core.get_objects_inside_radius(pos, 1);
    for i, obj in ipairs(objs) do
        if obj and obj:get_luaentity() then
            if (obj:get_luaentity().name == "warp_portal:gate") then
                return true;
            end;
        end;
    end;
    return false;
end;

-- BUTTON
core.register_node("warp_portal:button", {
    description = "Button",
    paramtype = "light", paramtype2 = "facedir",
    drawtype = "airlike",
    groups = { not_in_creative_inventory=1, },
    sunlight_propagates = true,
    walkable = false, pointable = true,
    diggable = false, buildable_to = false,
    floodable = false,
    light_source = 4, drop = "",
    selection_box = { type="fixed", fixed={{ -0.3, -0.5, 0.3,  0.3, 0.6, 0.5 }} },
    on_blast = function() end,
    on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
        pos = vector.new(pos.x, pos.y-1, pos.z);
        warp_portal.teleport_formspec(pos, clicker, get_status(pos));
        return itemstack;
    end,
    on_punch = function(pos, node, puncher, pointed_thing)
        pos = vector.new(pos.x, pos.y-1, pos.z);
        warp_portal.teleport_formspec(pos, puncher, get_status(pos));
    end,
});

-- ON RECEIVE FIELDS
core.register_on_player_receive_fields(function(player, formname, fields)
    
    if player and (formname:find("warp_portal:teleport_")) then
        local x, y, z = formname:match("warp_portal:teleport_(.-)_(.-)_(.*)");
        local pos = { x=tonumber(x), y=tonumber(y), z=tonumber(z) };
        
        for field in pairs(fields) do
            if field:find("warp_to_") then
                local new_x, new_y, new_z = string.sub(field, 9):match("(.-)|(.-)|(.*)");
                local new_pos = vector.new(tonumber(new_x), tonumber(new_y), tonumber(new_z));
                core.sound_play("warp", { pos=pos });
                player:set_pos(new_pos);
                core.sound_play("warp", { pos=new_pos });
                player:set_yaw(gate_yaw_map[core.get_node(pos).param2%4] or 0);
                core.close_formspec(player:get_player_name(), formname);
            end;
        end;
        
        if fields.portal_turn_on then
            core.add_entity(pos, "warp_portal:gate");
            for i, obj in ipairs(core.get_objects_inside_radius(pos, 1)) do
                if obj and obj:get_luaentity() then
                    local param2 = core.get_node(pos).param2;
                    if (obj:get_luaentity().name == "warp_portal:gate") then
                        obj:set_yaw(gate_yaw_map[param2%4] or 0);
                        core.set_node(
                            vector.new(pos.x, pos.y+2, pos.z), 
                            { name="warp_portal:light", param2=core.get_node(pos).param2 }
                        );
                        core.sound_play("turn_on", { pos=pos });
                    end;
                end;
            end;
            
            local meta = core.get_meta(pos);
            storage:set_string(meta:get_string("infotext"), x.."|"..y.."|"..z);
            warp_portal.teleport_formspec(pos, player, get_status(pos));
        end;
        
        if fields.portal_turn_off then
            local objs = core.get_objects_inside_radius(pos, 1);
            for i, obj in ipairs(objs) do
                if obj and obj:get_luaentity() then
                    if (obj:get_luaentity().name == "warp_portal:gate") then
                        core.set_node(
                            vector.new(pos.x, pos.y+2, pos.z), 
                            { name="warp_portal:hidden_part", param2=core.get_node(pos).param2 }
                        );
                        core.sound_play("turn_off", { pos=pos });
                        obj:remove();
                    end;
                end;
            end;
            
            local meta = core.get_meta(pos);
            storage:set_string(meta:get_string("infotext"), "off");
            warp_portal.teleport_formspec(pos, player, get_status(pos));
        end;
    end;
    
    if player and (formname:find("warp_portal:set_name_")) then
        local x, y, z = formname:match("warp_portal:set_name_(.-)_(.-)_(.*)");
        local pos = { x=tonumber(x), y=tonumber(y), z=tonumber(z) };
        
        if fields.portal_name_done or fields.portal_name then
            if (fields.portal_name == "") then
                warp_portal.set_name_formspec(
                    pos, player, fields.portal_name,
                    F(C("#FF5555", "Sorry, but you can't use empty name!"))
                );
            elseif (storage:get_string(fields.portal_name) == "") then
                core.chat_send_all(
                    player:get_player_name().." "..S("placed warp portal: ")..fields.portal_name
                );
                
                local meta = core.get_meta(pos);
                meta:set_string("infotext", fields.portal_name);
                
                warp_portal.storage_add(storage, fields.portal_name, x.."|"..y.."|"..z);
                
                core.add_entity(pos, "warp_portal:gate");
                core.add_entity(pos, "warp_portal:scroll");
                local objs = core.get_objects_inside_radius(pos, 1);
                for i, obj in ipairs(objs) do
                    if obj and obj:get_luaentity() then
                        local param2 = core.get_node(pos).param2;
                        if (obj:get_luaentity().name == "warp_portal:scroll") then
                            obj:set_yaw(yaw_map[param2%4] or 0);
                            obj:set_animation(anim_close, 30, anim_blend, false);
                        end;
                        if (obj:get_luaentity().name == "warp_portal:gate") then
                            core.sound_play("turn_on", { pos=pos });
                            obj:set_yaw(gate_yaw_map[param2%4] or 0);
                            core.set_node(
                                vector.new(pos.x, pos.y+2, pos.z), 
                                { name="warp_portal:light", param2=core.get_node(pos).param2 }
                            );
                        end;
                    end;
                end;
                
                core.close_formspec(player:get_player_name(), formname);
            else
                warp_portal.set_name_formspec(
                    pos, player, fields.portal_name,
                    F(C("#FF5555", "Sorry, but this name if already taken!"))
                );
            end;
        end;
    end;
end);


-- REGISTER RECIPE
local bricks = warp_portal.impact(
    nil, {
        ["minetest"] = "default:stonebrick",
        ["mineclonia"] = "mcl_core:stonebrick",
        ["mineclone2"] = "mcl_core:stonebrick",
        ["asuna"] = "default:stonebrick",
    }
);
local power = warp_portal.impact(
    nil, {
        ["minetest"] = "default:mese",
        ["mineclonia"] = "mcl_end:ender_eye",
        ["mineclone2"] = "mcl_end:ender_eye",
        ["asuna"] = "astralcraft:warp_star",
    }
);
local center = warp_portal.impact(
    nil, {
        ["minetest"] = "",
        ["mineclonia"] = warp_portal.var_impact(
            not storage.get_keys, 
            "mesecons_torch:redstoneblock", 
            "mcl_redstone_torch:redstoneblock"
        ),
        ["mineclone2"] = "mesecons_torch:redstoneblock",
        ["asuna"] = "default:mese",
    }
);
local strong = warp_portal.impact(
    nil, {
        ["minetest"] = "default:obsidian",
        ["mineclonia"] = "mcl_core:obsidian",
        ["mineclone2"] = "mcl_core:obsidian",
        ["asuna"] = "default:obsidian_block",
    }
);
local slab = warp_portal.impact(
    nil, {
        ["minetest"] = "stairs:slab_stone_block",
        ["mineclonia"] = "mcl_stairs:slab_deepslate_polished",
        ["mineclone2"] = "mcl_stairs:slab_deepslate_polished",
        ["asuna"] = "stairs:slab_stone_block",
    }
);
local lectern = warp_portal.impact(
    nil, {
        ["minetest"] = "default:wood",
        ["mineclonia"] = "mcl_lectern:lectern",
        ["mineclone2"] = "mcl_lectern:lectern",
        ["asuna"] = "default:wood",
    }
);

if (warp_portal.get_game_info().id == "repixture") and core.get_modpath("rp_crafting") then
    crafting.register_craft({
        output = "warp_portal:portal",
        items = {
            "rp_default:reinforced_cobble 2",
            "rp_default:block_wrought_iron 2",
            "rp_default:planks",
            "rp_lumien:block",
        }
    });
else
    core.register_craft({
        output = "warp_portal:portal",
        recipe = {
            {bricks, power,   bricks},
            {strong, center,  strong},
            {slab,   lectern, slab},
        },
    });
end;

-- GLOBALSTEP
core.register_globalstep(function(dtime)
    for _, player in ipairs(core.get_connected_players()) do
        local pos = player:get_pos();
        
        if (core.get_node(pos).name == "warp_portal:portal") then
            if (is_player_standing[player] == nil) then
                for i, obj in ipairs(core.get_objects_inside_radius(pos, 1)) do
                    if obj and obj:get_luaentity() then
                        if (obj:get_luaentity().name == "warp_portal:scroll") then
                            obj:set_animation(anim_open, 30, anim_blend, false);
                            core.set_node(
                                vector.new(pos.x, pos.y+1, pos.z), 
                                { name="warp_portal:button", param2=core.get_node(pos).param2 }
                            );
                        end;
                    end;
                end;
                is_player_standing[player] = pos;
            end;
        else
            if (is_player_standing[player] ~= nil) then
                for i, obj in ipairs(core.get_objects_inside_radius(is_player_standing[player], 1)) do
                    if obj and obj:get_luaentity() then
                        if (obj:get_luaentity().name == "warp_portal:scroll") then
                            pos = obj:get_pos();
                            obj:set_animation(anim_close, 30, anim_blend, false);
                            core.set_node(
                                vector.new(pos.x, pos.y+1, pos.z), 
                                { name="warp_portal:hidden_part"}
                            );
                        end;
                    end;
                end;
                is_player_standing[player] = nil;
            end;
        end;
    end
end)

-- ACHIEVEMENT
if core.get_modpath("mcl_achievements") then
    achvnt = awards;
elseif core.get_modpath("rp_achievements") then
    achvnt = achievements;
end;

if achvnt then
    achvnt.register_achievement(
        "warp_portal:portal", 
        warp_portal.table_merge({
            title = S("Let's Travel"),
            description = S("Craft the Warp Portal."),
        }, warp_portal.impact({}, {
            ["mineclonia"] = {
                icon = portal_icon,
                trigger = {
                    type = "craft",
                    item = "warp_portal:portal",
                    target = 1,
                },
                type = "Advancement",
                group = "Overworld",
            },
            ["mineclone2"] = {
                icon = portal_icon,
                trigger = {
                    type = "craft",
                    item = "warp_portal:portal",
                    target = 1,
                },
                type = "Advancement",
                group = "Overworld",
            },
            ["repixture"] = {
                times = 1,
                item_icon = "warp_portal:portal",
                craftitem = "warp_portal:portal",
                difficulty = 5,
            },
        }))
    );
end;
