--[[

   Hyperloop Mod
   =============

   Copyright (C) 2017-2019 Joachim Stolberg

   LGPLv2.1+
   See LICENSE.txt for more information

]]--

-- for lazy programmers
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
local P = minetest.string_to_pos
local M = minetest.get_meta

-- Load support for intllib.
local S = vents.S
local NS = vents.NS

local Tube = vents.Tube
local Stations = vents.Stations

-- Used to store the Station list for each vent:
--    tStationList[SP(pos)] = {pos1, pos2, ...}
local tStationList = {}

local function store_station(pos, placer)
   local facedir = vents.get_facedir(placer)
   -- do a facedir correction
   facedir = (facedir + 3) % 4  -- face to LCD
   Stations:set(pos, "Station", {
         owner = placer:get_player_name(),
         facedir = facedir,
         time_blocked = 0})
end

local function naming_formspec(pos)
   local formspec =
      "size[7,4.4]"..
      "label[0,0;"..S("Please enter the vent name.").."]" ..
      "field[0.2,1.5;7.1,1;name;"..S("Vent name")..";Vent]" ..
      "field[0.2,2.7;7.1,1;info;"..S("Additional vent information")..";]" ..
      "button_exit[2.5,3.7;2,1;exit;Save]"
   return formspec
end

-- Form spec for the station list
local function generate_string(sortedList)
   local tRes = {"size[12,8]"..
   "label[4,0; "..S("Select your destination").."]"}
   tRes[2] = "tablecolumns[text,width=20;text,width=6,align=right;text]"

   local stations = {}
   for idx,tDest in ipairs(sortedList) do
      local name = tDest.name or S("<unknown>")
      local distance = tDest.distance or 0
      local info = tDest.booking_info or ""

      stations[#stations+1] = minetest.formspec_escape(string.sub(name, 1, 28))
      stations[#stations+1] = distance.."m"
      stations[#stations+1] = minetest.formspec_escape(info)
   end

   if #stations>0 then
      tRes[#tRes+1] = "table[0,1;11.8,7.2;button;"..table.concat(stations, ",").."]"
   else
      tRes[#tRes+1] = "button_exit[4,4;3,1;button;Update]"
   end

   return table.concat(tRes)
end

local function store_station_list(pos, sortedList)
   local tbl = {}
   for idx,item in ipairs(sortedList) do
      tbl[#tbl+1] = item.pos
   end
   tStationList[SP(pos)] = tbl
end

local function remove_junctions(sortedList)
   local tbl = {}
   for idx,item in ipairs(sortedList) do
      if not item.junction and item.booking_pos then
         tbl[#tbl+1] = item
      end
   end
   return tbl
end

local function filter_subnet(sortedList, subnet)
   if vents.subnet_enabled then
      if subnet == "" then
         subnet = nil
      end

      local tbl = {}
      for idx,item in ipairs(sortedList) do
         if item.subnet == subnet then
            tbl[#tbl+1] = item
         end
      end
      return tbl
   end
   return sortedList
end

-- Used to update the station list for booking machine
-- and teleport list.
local function station_list_as_string(pos, subnet)
   local meta = M(pos)
   -- Generate a name sorted list of all connected stations
   local sortedList = Stations:station_list(pos, pos, "name")
   -- remove all junctions from the list
   sortedList = remove_junctions(sortedList)
   -- use subnet pattern to reduce the list
   sortedList = filter_subnet(sortedList, subnet)
   -- store the list for later use
   store_station_list(pos, sortedList)
   -- Generate the formspec string
   return generate_string(sortedList)
end

minetest.register_on_player_receive_fields(function(player, formname, fields)
   local name = player:get_player_name()
   local pos = vents.player_pos[name]
   if formname == 'vents:setup' then
      if fields.exit then
         if fields.name ~= nil then
            local station_name = string.trim(fields.name)
            if station_name == "" then
               return
            end
            if Stations:get(pos).booking_pos then
               minetest.chat_send_player(name, S("Remove and replace to change name."))
               return
            end
               -- store meta and generate station formspec
            Stations:update(pos, {
                  name = station_name,
                  booking_pos = pos,
                  booking_info = string.trim(fields.info),
            })

            local meta = minetest.get_meta(pos)
            meta:set_string("infotext", "Station: "..station_name)
            minetest.chat_send_player(name, 'Saving data')
         else
            vents.chat(player, S("Invalid station name!"))
         end
      end
   elseif formname == 'vents:station_list' then
      local te = minetest.explode_table_event(fields.button)
      local idx = tonumber(te.row)
      if idx and te.type=="CHG" then
         local tStation, src_pos = vents.get_base_station(pos)
         local dest_pos = tStationList[SP(pos)] and tStationList[SP(pos)][idx]
         if dest_pos and tStation then
            local air = minetest.find_node_near(dest_pos, 2, {'air'})
            if air then
               player:set_pos(air)
            else
               minetest.chat_send_player(name, 'Sorry, can\'t teleport you there.')
            end
            --Should there be some sort of freeze on movement or health loss for using the vents?
         end
         minetest.close_formspec(player:get_player_name(), formname)
      end
   end
end)

local function on_destruct(pos)
   Stations:update((pos), {
      booking_pos = "nil",
      booking_info = "nil",
      name = "Station",
   })
end

minetest.register_node("vents:station", {
   description = S("Hyperloop Station Block"),
   drawtype = "nodebox",
   tiles = {
      "hyperloop_station.png",
      "hyperloop_station_connection.png",
      "hyperloop_station_connection.png",
   },

   after_place_node = function(pos, placer, itemstack, pointed_thing)
      vents.check_network_level(pos, placer)
      M(pos):set_string("infotext", S("Access Point"))
      store_station(pos, placer)
      Tube:after_place_node(pos)
   end,

   on_rightclick = function(pos, node, clicker)
      local name = clicker:get_player_name()
      local player_attributes = clicker:get_meta()
      local mode = player_attributes:get_string('mode')
      vents.player_pos[name] = pos
      if mode == 'traitor' then
         minetest.show_formspec(name, 'vents:station_list', station_list_as_string(pos))
      elseif mode == 'builder' then
         minetest.show_formspec(name, 'vents:setup', naming_formspec(pos))
      end
   end,

   after_dig_node = function(pos, oldnode, oldmetadata, digger)
      Tube:after_dig_node(pos)
      Stations:delete(pos)
   end,

   on_destruct = on_destruct,
   on_rotate = screwdriver.disallow,
   paramtype2 = "facedir",
   groups = {breakable=1},
   is_ground_content = false,
   sounds = {footstep = {name = 'metal', gain = 1}},
})
