-- API FILE

-- Does not include:
    -- Sonic screwdriver (sonic.lua)
    -- Exterior registering (tardis/exterior_api.lua) 

-- Does include:
    -- Place Tardis interior
    -- Rebuild Tardis interior
    -- Locate Tardis

--Planned:
    -- Summon Tardis API
    -- Waypoints
    -- Power
    -- and more

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

-- Returns the default depth Tardis interiors are placed at
function drwho_tardis.get:interior_depth()
    return _drwho_tardis.interiorDepth
end

-- Returns the interior type of a player's Tardis 
function drwho_tardis.get:interior_type(player)
    local type = data:get_string(player.."type") or "old"
    return type
end

-- Returns the exterior position of a player's Tardis (locate_tardis)
function drwho_tardis.get:tardis_ext_position(player)
    local out_pos = minetest.deserialize(data:get_string(player.."out_pos"))
    if out_pos == nil or out_pos == "" then
        return false, "They don't have a Tardis!", nil -- sucess, errmsg, value
    end
    return true, "", out_pos
end 

-- Returns the Tardis exterior list, or an empty table if for some reason "exterior_list" doesn't exist
function drwho_tardis.get:exterior_list()
    local ext_list = minetest.deserialize(data:get_string("exterior_list")) or {}
    return ext_list
end

-- Returns exterior information based on it's index in the exterior_list
function drwho_tardis.get:exterior_info(index)
    local ext_list = drwho_tardis.get:exterior_list()
    return ext_list[index] -- if it's nil that's sad
end

-- Returns the current power levels of a player's Tardis
function drwho_tardis.get:power(player)
    return false, "This function has not been made yet", nil
end

-------------------------------------------------------------------------------------------------------
----------------------      PRIVATE API FUNCTIONS       -----------------------------------------------
-------------------------------------------------------------------------------------------------------

-- [PRIVATE] Places a Tardis interior for a player 
function _drwho_tardis:place_tardis_interior(player, type, pos)
    if pos == nil then 
        return false, "Position not provided!", nil
    else
        if type == "custom" then
            -- Assume that the given pos is where the door is going to be
            data:set_string(player.."type", type) -- Save the fact their interior is custom
            data:set_string(player.."door_pos_table", minetest.serialize({door_0 = pos})) -- Remember the door position
            data:set_string(player.."door_offset_table", minetest.serialize({door_0={x=0,y=0,z=0}})) -- door offset is 0
            data:set_string(player.."in_pos", minetest.serialize(pos)) -- save new interior position
            return true, "", {door_0 = pos} -- success, message, door_pos_table
        end
        pos.y = _drwho_tardis.interiorDepth -- set the y pos of the interior
        local door_0_offset = {x=0,y=0,z=0}
        local door_offset_table = {}
        local door_pos_table = {}
        if _drwho_tardis.rooms[type] then
			minetest.place_schematic(pos, _drwho_tardis.rooms[type].path) --minetest.get_modpath("drwho_tardis") .. "/schems/console_room.mts"
            door_0_offset = _drwho_tardis.rooms[type].door_0_offset --  = {x=7,y=2,z=16}
            local d0_pos = vector.add(pos, door_0_offset) -- Schematic adjustments so that the player gets teleported right in front of the door
            door_pos_table = { door_0 = d0_pos, door_1=nil, door_2=nil }
            door_offset_table = { door_0 = door_0_offset, door_1=nil, door_2=nil }    

            if _drwho_tardis.rooms[type].door_1_offset then
                local d1_pos = vector.add(pos, _drwho_tardis.rooms[type].door_1_offset)
                door_offset_table.door_1 = _drwho_tardis.rooms[type].door_1_offset
                door_pos_table.door_1 = d1_pos
            end
            if _drwho_tardis.rooms[type].door_2_offset then
                local d2_pos = vector.add(pos, _drwho_tardis.rooms[type].door_2_offset)
                door_offset_table.door_1 = _drwho_tardis.rooms[type].door_1_offset
                door_pos_table.door_2 = d2_pos
            end
        else
			return false, "Invalid interior room type! Use /tardis_int to change it.", type -- success, errmsg, int_type
        end
        data:set_string(player.."type", type) -- Save what type of interior they have
        data:set_string(player.."door_pos_table", minetest.serialize(door_pos_table)) -- Remember the door positions
        data:set_string(player.."door_offset_table", minetest.serialize(door_offset_table)) -- Remember the door offsets
        data:set_string(player.."console_place_pos", minetest.serialize(pos)) -- Remember the door offsets
        data:set_string(player.."in_pos", minetest.serialize(door_pos_table.door_0)) -- save new interior position
        --minetest.log("api.lua 105: "..minetest.serialize(door_pos_table))
        return true, "", door_pos_table -- success, message, door_pos_table
    end
end

-- id is the username of the player who owns the Tardis
function _drwho_tardis:init_interior_doors(id, door_pos_table)

    local d0meta = minetest.get_meta(door_pos_table.door_0)
    local d0timer = minetest.get_node_timer(door_pos_table.door_0)
    d0timer:start(0.2) --start door timer (in case it doesn't start on construct)
    d0meta:set_string("id", id) --set door id
    d0meta:set_string("type", "exit") -- set door type
    -- Some interiors have extra doors
    if door_pos_table.door_1 then
        local d1meta = minetest.get_meta(door_pos_table.door_1)
        local d1timer = minetest.get_node_timer(door_pos_table.door_1)
        d1meta:set_string("id", id)
        d1meta:set_string("type", "door_1")
        d1timer:start(0.2)
    end
    if door_pos_table.door_2 then 
        local d2meta = minetest.get_meta(door_pos_table.door_2)
        local d2timer = minetest.get_node_timer(door_pos_table.door_2)
        d2meta:set_string("id", id)
        d2meta:set_string("type", "door_2")
        d2timer:start(0.2)
    end
    return true, door_pos_table
end

-- [PRIVATE] Rebuilds a player's Tardis (rebuild_tardis)
-- Can fix old interiors being too high up in the world, and fix caves running through them
function _drwho_tardis:rebuild_tardis_interior(player)

    local in_pos = minetest.deserialize(data:get_string(player.."in_pos"))
    if in_pos == "" or in_pos == nil then 
        return false, "You don't have a Tardis!" -- success, errmsg
    end

    local type = data:get_string(player.."type")
    if type=="" or type==nil then type = "default" end -- defaults
    local door_offset_table = minetest.deserialize(data:get_string(player.."door_offset_table"))
    if door_offset_table == nil or door_offset_table == "" or door_offset_table == {} then
        door_offset_table = {door_0=_drwho_tardis.rooms[type].door_0_offset, door_1 = _drwho_tardis.rooms[type].door_1_offset, door_2 = _drwho_tardis.rooms[type].door_2_offset}
        data:set_string(player.."door_offset_table", minetest.serialize(door_offset_table))
    end
    local door_0_offset = door_offset_table.door_0
    if door_0_offset=="" or door_0_offset==nil then door_0_offset = {x=7,y=2,z=16} end -- defaults

    -- This ensures that the old and new line up properly
    local anchor_pos = vector.subtract(in_pos, door_0_offset)
    local success, msg, door_pos_table = _drwho_tardis:place_tardis_interior(player, type, anchor_pos)

    if success == false then 
        return false, msg, door_pos_table -- door_pos_table is actually the type in this situation after returned by the place API function
    end

    -- Interior door stuff
    _drwho_tardis:init_interior_doors(player, door_pos_table)

    -- Clear Time Rotor pos because that doesn't exist anymore
    data:set_string(player.."r_pos", "")

    return true, "", door_pos_table -- success!
end

-- Check if the Time Rotor is there or not
-- name is required, pos is not
function _drwho_tardis.is_time_rotor(name, pos)
    local r_pos = pos or minetest.deserialize(data:get_string(name.."r_pos"))
    if not r_pos or r_pos == "" or r_pos == {} then return false, "r_pos doesn't exist", r_pos end
    local rmeta = minetest.get_meta(r_pos)
    local rotor_node = minetest.get_node(r_pos)
    
    if minetest.get_item_group(rotor_node.name, 'tardis_time_rotor') == 1 or 
        minetest.get_item_group(rotor_node.name, 'tardis_time_rotor_active') == 1 then
        return true, "rotor is there", r_pos
    else
        return false, "rotor is not there", r_pos
    end
end

-- Starts/stops time rotor animation
function _drwho_tardis.switch_time_rotor(name, on_off)
	-- 'r' is the Rotor
	local r_pos = minetest.deserialize(data:get_string(name.."r_pos"))
    local is_it_there, msg = _drwho_tardis.is_time_rotor(name)

    if is_it_there then
        local rmeta = minetest.get_meta(r_pos)
        local style = rmeta:get_string("style")
        local rotor_node = minetest.get_node(r_pos)
        if on_off == "on" then 
            minetest.swap_node(r_pos, {name = "drwho_tardis:rotor_active"..style })
            return true, r_pos
        elseif on_off == "off" then
            minetest.swap_node(r_pos, {name = "drwho_tardis:rotor"..style })
            return true, r_pos
        end
    else
        minetest.chat_send_player(name, S("You need to have a Time Rotor!"))
        return false, r_pos -- Time rotor is not there!
    end
end

-- locks or unlocks the interior door. if on_off is not given, it will toggle.
function _drwho_tardis.toggle_int_door(name, on_off)
    local in_pos = minetest.deserialize(data:get_string(name.."in_pos"))

    if on_off == "lock" then
        minetest.set_node(in_pos, { name = "drwho_tardis:in_door_locked" }) 
        local dmeta = minetest.get_meta(in_pos)
        dmeta:set_string("id", name) -- set door id
        dmeta:set_string("type", "exit") -- set door type 
        return true, "Interior door locked"
    elseif on_off == "unlock" then 
        minetest.set_node(in_pos, { name = "drwho_tardis:in_door" })
        -- Start door timer
        local dtimer = minetest.get_node_timer(in_pos)
        local dmeta = minetest.get_meta(in_pos)
        dmeta:set_string("id", name) -- set door id 
        dmeta:set_string("type", "exit") -- set door type 
        dtimer:start(0.2) -- start door timer (in case it doesn't start on construct)
        return true, "Interior door unlocked"
    else -- on_off is not given, toggle door
        if minetest.get_node(in_pos).name == "drwho_tardis:in_door" then
            minetest.set_node(in_pos, { name = "drwho_tardis:in_door_locked" }) 
            local dmeta = minetest.get_meta(in_pos)
            dmeta:set_string("id", name) -- set door id
            dmeta:set_string("type", "exit") -- set door type 
           return true, "Interior door locked"
        else
            minetest.set_node(in_pos, { name = "drwho_tardis:in_door" })
            -- Start door timer
            local dtimer = minetest.get_node_timer(in_pos)
            local dmeta = minetest.get_meta(in_pos)
            dmeta:set_string("id", name) -- set door id 
            dmeta:set_string("type", "exit") -- set door type 
            dtimer:start(0.2) -- start door timer (in case it doesn't start on construct)
            return true, "Interior door unlocked"
        end
    end

end

-- Toggle the antigrav system
-- returns success, status
function _drwho_tardis.toggle_mavity(name)
    if data:get_string(name.."respect_mavity") == nil then -- Old version Tardis or doesn't have one
        data:set_string(name.."respect_mavity", "yes")
        return true, "Antigrav system enabled but won't sink in water"
    elseif data:get_string(name.."respect_mavity") == "yes" then -- It's on, turn it off
        data:set_string(name.."respect_mavity", "no")
        return true, "Antigrav system disabled"
    elseif data:get_string(name.."respect_mavity") == "no" then
        data:set_string(name.."respect_mavity", "sink") -- sink in liquids mode
        return true, "Antigrav system enabled and set to sink in liquids"
    else
        data:set_string(name.."respect_mavity", "yes") -- turn it on
        return true, "Antigrav system enabled but won't sink in water"
    end
end