-- CONTENTS
-- Digilines formspec
-- generate_sonic_screwdriver()
-- Generic Sonic Screwdriver

local S = minetest.get_translator("drwho_tardis")
local data = minetest.get_mod_storage()

local function get_formspec_digilines()
    return "size[10,10]"..
		"field[1,1;8,1;sonic_message;Message;]"..
		"field[1,3;8,1;sonic_channel;Channel;]"..
		"button_exit[1,6;3,3;sonic_send;Send]"
end

local sonictypes = 4 --number of variants for end, middle, and top
-- register_all_random_sonic_screwdrivers(sonictypes) is run after it is defined


local function add_sonictypes_texture(item, top, mid, bottom)
    local sonicstack = ItemStack(item)
    sonicstack:get_meta():set_string("inventory_image", "sonic_screwdriver.png^sonic_top_"..top..".png^sonic_mid_"..mid..".png^sonic_end_"..bottom..".png")
end    



-- See API.md for more details
function drwho_tardis:register_sonic_device(def)
    --def = {gentype, texture, top, mid, bottom, sonictype, customname, customdescription, no_creative}
    

    -- create random values 
    -- If they are specified in def.top etc these values are overridden
    local top = math.random(1,sonictypes) or def.top
    local mid = math.random(1,sonictypes) or def.mid
    local bottom = math.random(1,sonictypes) or def.bottom

    -- Only run if given values are defined
    -- Makes sure that def.top is not above sonictypes as that would result in error
    if top > sonictypes then top = 1 end
    if mid > sonictypes then mid = 1 end
    if bottom > sonictypes then bottom = 1 end
    minetest.chat_send_all(sonictypes.." top: "..top.." mid: "..mid.." bottom: "..bottom)

    
    if def.sonictype == nil then def.sonictype = "screwdriver" end -- Default to "screwdriver"
    
    local item_name = ""
    if def.customname ~= nil then 
        item_name = def.customname
    else
        item_name = ":drwho_tardis:sonic_"..def.sonictype.."_"..top.."_"..mid.."_"..bottom
    end
    minetest.chat_send_all(".")
    -- if playername is not given, custom description must be given. 
    --local item_description = def.customdescription or def.playername.."\'s Sonic Screwdriver"
    local item_description = def.customdescription or S("Sonic Screwdriver (@1-@2-@3)", top, mid, bottom) -- (4-2-3)

    local inventoryimage = ""
    if def.gentype == "custom" then
        inventoryimage = def.texture
    else -- Stacked image using top, mid and bottom.
        inventoryimage = "sonic_screwdriver.png^sonic_top_"..top..".png^sonic_mid_"..mid..".png^sonic_end_"..bottom..".png"
    end
    
    
    -- Add to appropriate item groups
    def.groups = def.groups or {}
    def.groups.sonic = 1
    def.groups.sonic_screwdriver = def.sonictype == "screwdriver" and 1 or nil -- is sonic a screwdriver? otherwise do not add
    def.groups.not_in_creative_inventory = def.no_creative == true and 1 or nil

    -- set up tool capabilities
    local toolcaps = def.tool_capabilities or {}
    toolcaps.full_punch_interval = toolcaps.full_punch_interval or 0.5
    toolcaps.groupcaps = toolcaps.groupcaps or {}
    toolcaps.groupcaps.sonic = toolcaps.groupcaps.sonic or {}
    local sonicgc = toolcaps.groupcaps.sonic
    sonicgc.uses = sonicgc.uses or 1000 -- the higher, more durability
    sonicgc.maxlevel = sonicgc.maxlevel or 1 -- the higher, more durability

    -- sounds
    def.sounds = def.sounds or {}
    def.sounds.sonic_use = def.sounds.sonic_use or {name="sonic_sound",max_hear_distance = 10}
    

    minetest.register_tool(item_name, {
        description = item_description,
        inventory_image = inventoryimage,
        stack_max=1,
        groups = def.groups,
        tool_capabilities = toolcaps,
        

        --------------------- ACTUAL SONIC CODE BELOW -----------------------

        on_use = function(itemstack, player, pointed_thing)
        local pmeta = player:get_meta()
        local id = player:get_player_name()
        local imeta = itemstack:get_meta()
        imeta:set_string("interact", "no")
        local select_pos = player:get_pos()
        local sonic_add_wear = true
        if pointed_thing.type == "node" then
            local controls = player:get_player_control()
            local node = minetest.get_node(pointed_thing.under)
            local meta = minetest.get_meta(pointed_thing.under)
            select_pos = pointed_thing.under
            local drop = minetest.get_node_drops(node, nil)
            if controls.sneak then
              -- sneaking
                local r_pos = minetest.deserialize(data:get_string(id.."r_pos"))
                if not r_pos then
                    minetest.chat_send_player(id, S("You do not have a Tardis!")) 
                elseif r_pos.x+50 > select_pos.x and r_pos.x-50 < select_pos.x and r_pos.z+50 > select_pos.z and r_pos.z-50 < select_pos.z and r_pos.y+50 > select_pos.y and r_pos.y-50 < select_pos.y then minetest.chat_send_player(id, "Your Tardis can not be summoned here!") else
                    if data:get_int(id.."power") < 3 then minetest.chat_send_player(id, "Not Enough Power In Tardis!") else
                        local select_pos = pointed_thing.above
                        local out_pos = minetest.deserialize(data:get_string(id.."out_pos"))
                        local look = data:get_string(id.."look")
                        minetest.set_node(out_pos, {name = "air"})
                        out_pos.x = select_pos.x
                        out_pos.y = select_pos.y
                        out_pos.z = select_pos.z
                            
                        out_pos.y = out_pos.y+1
                        minetest.set_node(out_pos, {name = "air"})
                        out_pos.y = out_pos.y-1
                        
                        minetest.set_node(out_pos, {name=look})
                        local ometa = minetest.get_meta(out_pos)
                        ometa:set_string("id", id)
                        data:set_string(id.."out_pos", minetest.serialize(out_pos))
                        data:set_int(id.."power", data:get_string(id.."power")-3)
                        local timer = minetest.get_node_timer(out_pos)
                        timer:start(0.5)
                        minetest.chat_send_player(id, S("Tardis Summoned"))
                    end
                end
            else
              -- not sneaking
                if id == meta:get_string("id") then
                    if minetest.get_item_group(node.name, "tardis") == 1 then
                        local look = data:get_string(id.."look")
                        minetest.set_node(select_pos, {name = look.."_locked"})
                        local ometa = minetest.get_meta(select_pos)
                        ometa:set_string("id", id)
                        minetest.chat_send_player(id, S("Tardis Locked"))
                    end
                    if minetest.get_item_group(node.name, "tardis_locked") == 1 then
                        local look = data:get_string(id.."look")
                        minetest.set_node(select_pos, {name = look})
                        local timer = minetest.get_node_timer(select_pos)
                        timer:start(0.2)
                        local ometa = minetest.get_meta(select_pos)
                        ometa:set_string("id", id)
                        minetest.chat_send_player(id, S("Tardis Unlocked"))
                    end
                elseif string.match(node.name,"doors:door_steel") then
                    doors.door_toggle(select_pos, nil, nil)
                elseif string.match(node.name,"doors:trapdoor_steel") then
                    doors.trapdoor_toggle(select_pos, nil, nil)
                elseif node.name == "default:glass" then
                    minetest.set_node(select_pos, {name = "default:obsidian_glass"})
                elseif node.name == "default:dirt" then
                    minetest.set_node(select_pos, {name = "default:dirt_with_grass"})
                elseif node.name == "default:sand" then
                    minetest.set_node(select_pos, {name = "default:silver_sand"})
                elseif node.name == "default:obsidian" then
                    minetest.set_node(select_pos, {name = "default:lava_source"} )
                elseif minetest.get_item_group(node.name, "mesecon_conductor_craftable") == 1 then
                    if mesecon.is_conductor_on(node) then
                        mesecon.turnoff(select_pos, {0, 0, 0})
                    else
                        mesecon.turnon(select_pos, {0, 0, 0})
                    end
                elseif drop[1] == "digilines:wire_std_00000000" then
                    local meta = player:get_meta()
                    meta:set_string("sonic_digiline", minetest.serialize(select_pos))
                    minetest.show_formspec(player:get_player_name(), "drwho_tardis:digilines_formspec", get_formspec_digilines() )
                else
                    sonic_add_wear = false -- don't reduce durability
                    --minetest.chat_send_player(id, "not reducing durability")
                    return -- we can't do anything with this node
                end
            end
        elseif pointed_thing.type == "object" then
            local controls = player:get_player_control()
            local obj = pointed_thing.ref
            if controls.sneak then 
                local vpos = obj:get_pos()
                select_pos = vpos
                local pos = player:get_pos()
                if obj:is_player() then 
                    obj:add_player_velocity({ x = 5*(vpos.x - pos.x), y = 5*(vpos.y - pos.y), z = 5*(vpos.z - pos.z)})
                else
                    obj:add_velocity({ x = 5*(vpos.x - pos.x), y = 5*(vpos.y - pos.y), z = 5*(vpos.z - pos.z)})
                end
            else 
                minetest.chat_send_player(id, S("Scanned subject had @1 hearts", obj:get_hp()/2))
            end
        else
            sonic_add_wear = false -- don't reduce durability
            --minetest.chat_send_player(id, "not reducing durability")
            return -- not pointing to anything important, return
        end
        local sonic_sound = table.copy(def.sounds.sonic_use)
        sonic_sound.pos = select_pos
        minetest.sound_play(sonic_sound.name, sonic_sound)
        if sonic_add_wear == true then
            itemstack:add_wear(65535 / (sonicgc.uses * (3^(sonicgc.maxlevel-1) ) - 1 ))
            --minetest.chat_send_player(id, "durability added")
        end
        return itemstack
    end -- End Sonic Device on_use
    })-- end Sonic Device tool registration

    ----------------------------- Post Registration Code ----------------------------------
---[[
    if def.gentype == "custom" then
        -- do nothing, it has already been set
    else -- if == "choose", == "random", or not specified
        add_sonictypes_texture(item_name, top, mid, bottom) -- Add correct textures using ItemStack
        minetest.chat_send_all(S("Sonic Screwdriver registered with textures @1-@2-@3 and itemstring @4", top, mid, bottom, item_name))
    end
--]]
    minetest.register_alias(item_name:match"^.-:(.+)", item_name) -- Just in case
    minetest.register_alias(item_name, item_name:match"^.-:(.+)") -- Who knows? 

return item_name
end



-- Loop through and register every random texture screwdriver, based on sonictypes value.
-- Should run each time game starts so if in a recent update new textures have been added
-- and sonictypes is increased, all the new ones will be registered automatically.
-- It also overwrites every other one, but that doesn't affect anything as it's still the same itemstring.
-- I made this before I realised that the actual error was because I was using hyphens.
local function register_all_random_sonic_screwdrivers(sonictypes)
    -- NAMING SCHEME
    -- drwho_tardis: sonic _screwdriver _ top _ mid _ end
    -- e.g. "drwho_tardis:sonic_screwdriver_1_4_3"
    -- IMPORTANT: It cannot have any hyphens (-) in the itemstring^^! So I use underscores
    -- Sonic Screwdriver (top - mid - end)
    -- e.g. "Sonic Screwdriver (1-4-3)"

    -- We don't want (sonictypes)^3 diffferent Sonic Screwdrivers flooding the inventory!
    -- So these all have no_creative = true

    -- Each loop of topCount does every sonic with 1-n-n, then 2-n-n, until sonictypes is reached.
    -- There are 3 loops, and they each do it (sonictypes) times, therefore (sonictypes)^3 which is exactly how many we have
    for topCount = 1, sonictypes do
        for midCount = 1, sonictypes do
            for endCount = 1, sonictypes do
                drwho_tardis:register_sonic_device({
                    gentype = "choose", -- Choose the combination of parts
                    sonictype = "screwdriver", -- Specify it is a screwdriver
                    top = topCount, -- Choose top texture 
                    mid = midCount, -- Choose middle texture 
                    bottom = endCount, -- Choose bottom texture 
                    customname = "drwho_tardis:sonic_screwdriver_"..topCount.."_"..midCount.."_"..endCount, -- Custom name
                    customdescription = S("Sonic Screwdriver (@1-@2-@3)",topCount, midCount, endCount), -- Custom player-facing item name 
                    no_creative = true, -- DON'T show up in the creative inventory - see above comment
                })
            end
        end
    end 


end

register_all_random_sonic_screwdrivers(sonictypes)

-- Generic Sonic Screwdriver
drwho_tardis:register_sonic_device({
    gentype = "choose", -- Choose the combination of parts
    sonictype = "screwdriver", -- Specify it is a screwdriver
    top = 1, -- Choose top texture 
    mid = 1, -- Choose middle texture 
    bottom = 1, -- Choose bottom texture 
    customname = "drwho_tardis:sonic_screwdriver", -- Custom name
    customdescription = S("Sonic Screwdriver"), -- Custom player-facing item name 
    no_creative = false, -- The only screwdriver to show up in the creative inventory
})