local mod_name = core.get_current_modname()
local mod_path = core.get_modpath(mod_name)
local S = core.get_translator(mod_name)


aom_wire.debug_enable = false
local debugtags = {
    all = true
}

function aom_wire.debug(tag, text, color)
    if not text then text = tag; tag = nil end
    if not aom_wire.debug_enable then return end
    if not debugtags[tag or "all"] then return end
    if color then text = core.colorize(color, text) end
    core.log(text)
end


function aom_wire.get_eyepos(player)
    local eyepos = vector.add(player:get_pos(), vector.multiply(player:get_eye_offset(), 0.1))
    eyepos.y = eyepos.y + player:get_properties().eye_height
    return eyepos
end

function aom_wire.dist2(p1, p2)
    return (p1.x-p2.x)^2 + (p1.y-p2.y)^2 + (p1.z-p2.z)^2
end

function aom_wire.update_meta_uid(pos)
    local meta = core.get_meta(pos)
    local uid = meta:get_string("uid")
    if uid ~= "" then return uid end
    local node = core.get_node(pos)
    local ndef = core.registered_nodes[node.name]
    -- don't set meta on non-wire nodes
    if ndef and (not ndef._on_wire_input) and (core.get_item_group(node.name, "wire_output") < 1) then
        core.log("warning", "[aom_wire.update_meta_uid] tried to add a UID to a non wire node at " .. tostring(pos))
        return "%NONE"
    end
    if uid == "" then
        uid = string.format("%X", math.random(16^4-1))
        meta:set_string("uid", uid)
    end
    return uid
end

function aom_wire.get_meta_uid(pos)
    return core.get_meta(pos):get_string("uid")
end


function aom_wire.activate_node_outputs(pos, player, payload)
    -- go through each output
    -- aom_climate.debug_particle(pos, "#ff0", 1, vector.new(0, 6, 0), 5)
    local outputs = aom_wire.get_outputs(pos)
    local uid = aom_wire.update_meta_uid(pos)
    for i, offset in ipairs(outputs or {}) do repeat
        -- if node has moved, abort
        local new_uid = aom_wire.get_meta_uid(pos)
        if uid ~= new_uid then return false end
        local p = pos + offset
        local n = core.get_node_or_nil(p)
        if not n then break end
        local ndef = core.registered_nodes[n.name]

        -- aom_climate.debug_particle(p, "#0f0", 1, vector.new(0, 2, 0), 5)
        if ndef._on_wire_input ~= nil then
            if ndef._on_wire_input(p, player, pos, payload) == false then
                return false
            end
        end
    until true end
    if uid ~= aom_wire.get_meta_uid(pos) then return false end
    return true
end

function aom_wire.activate(pos, player, payload)
    return aom_wire.activate_node_outputs(pos, player, payload)
end

function aom_wire.add_output(pos, target, max_targets)
    local offset = vector.subtract(target, pos)
    local meta = core.get_meta(pos)
    -- by definition, can only link from output nodes
    local outputs = aom_wire.get_outputs(pos) or {}
    local has_already = false
    for k, v in ipairs(outputs) do
        if vector.equals(v, offset) then
            aom_wire.debug("info", "already stored")
            has_already = true
            table.remove(outputs, k)
            break
        end
    end
    if not has_already then
        if #outputs >= max_targets then
            aom_wire.debug("info", "too many outputs already")
            return false
        else
            table.insert(outputs, offset)
        end
    end
    meta:set_string("aom_wire_targetv2", core.serialize(outputs))
    aom_wire.debug("info", "stored")
    return true
end

local function _convert_from_datastringv1(pos, meta)
    local datastringv1 = meta:get_string("aom_wire_target")
    if datastringv1 == "" then return end
    local new_data = ((datastringv1 ~= "") and core.deserialize(datastringv1, true)) or nil
    if not new_data then return end

    for k, v in ipairs(new_data) do
        new_data[k] = vector.subtract(v, pos)
    end

    local datastringv2 = core.serialize(new_data)

    meta:set_string("aom_wire_target", "")
    meta:set_string("aom_wire_targetv2", datastringv2)
    return true
end

function aom_wire.get_outputs(pos, meta)
    meta = meta or core.get_meta(pos)
    if not meta then
        aom_wire.debug("info", "no meta")
        return nil
    end
    _convert_from_datastringv1(pos, meta)
    local datastring = meta:get_string("aom_wire_targetv2")
    local data = ((datastring ~= "") and core.deserialize(datastring, true)) or nil
    return data
end
