
local swappers = {}

minetest.register_entity(":artifact:color_swapper_display", {
    initial_properties = {
        visual = "mesh",
        mesh = "artifact_color_swapper.gltf",
        textures = {"artifact_color_swapper_base.png", "artifact_color_swapper_cube.png"},
        selectionbox = {
            -0.2, -0.2, -0.2,
            0.2, 0.2, 0.2
        },
        collisionbox = {
            -0.2, -0.2, -0.2,
            0.2, 0.2, 0.2
        },
        use_texture_alpha = true,
        shaded = false,
        glow = 10,
    },
    _interact_time = 0.4,
    _can_interact = function(e, m)
        return m.character == "key"
    end,
    on_activate = function(e, data)
        local pos = e.object:get_pos()
        local node = minetest.get_node(pos)
        if not node.name:find "color_swapper" then
            e.object:remove()
            return
        end
        e.object:set_armor_groups{immortal = 1}
        extend(e, minetest.deserialize(data) or {})
        
        if e.rotation then
            e:rotate(e.rotation)
        else
            e:rotate(vector.zero())
        end
        e._name = ""..math.random()
                
        local nm = minetest.get_meta(e.object:get_pos())
        local color = nm:get("color") or e.color
        
        e:set_color(color)
        
        swappers[e.object:get_pos():round():to_string()] = e
    end,
    on_deactivate = function(e)
        if e.particles then
            minetest.delete_particlespawner(e.particles)
        end
        swappers[e.object:get_pos():round():to_string()] = nil
    end,
    get_staticdata = function(e)
        return minetest.serialize{rotation = e.rotation, color = e.color}
    end,
    on_interact = function(e, m)
        e._no_interact = true
        e.object:set_animation({x=7,y=8}, 1, 1, false)
        m:set_color(e.color)
        artifact.play_sound {
            name = "artifact_color_swap",
            pos = e.object:get_pos(),
            range = 8,
        }
        minetest.after(1, function()
            if not e._active then return end
            e.object:set_animation({x=0,y=6}, 1, 1, true)
            e._no_interact = nil
        end)
    end,
    on_step = function(e)
        local found = artifact.sidekick.pos and artifact.sidekick.character == "key" and artifact.sidekick.pos:distance(e.object:get_pos()) < 6
        for _, m in pairs(artifact.players) do
            if m.character == "key" and m.object:get_pos():distance(e.object:get_pos()) < 6 then
                found = true
                break
            end
        end
        if found and e._active ~= true then
            e._active = true
            e.object:set_animation({x=9.5,y=10}, 1, 1, false)
            local pos = e.object:get_pos()
            e.particles = minetest.add_particlespawner {
                pos = {
                    min = pos:offset(-0.5,-0.5,-0.5),
                    max = pos:offset(0.5,0.5,0.5)
                },
                vel = {
                    min = vector.new(-1,-1,-1) *0.5,
                    max = vector.new(1,1,1) *0.5
                },
                size = 0.25,
                texture = {
                    name = "artifact_light_"..e.color..".png",
                    alpha_tween = {1,0}
                },
                time = 0,
                amount = 5,
                attract = {
                    kind = "point",
                    origin = pos,
                    strength = 0.5,
                }
            }
            minetest.after(0.5, function()
                if not e._active then return end
                e.object:set_animation({x=0,y=6}, 1, 1, true)
                e._no_interact = nil
            end)
        elseif not found and e._active ~= false then
            e._active = false
            e.object:set_animation({x=9,y=9.5}, 1, 1, false)
            if e.particles then
                minetest.delete_particlespawner(e.particles)
                e.particles = nil
            end
        end
    end,
    -- Let us update the color live in debug mode.
    on_punch = artifact.debug and function(e)
        local nm = minetest.get_meta(e.object:get_pos())
        local color = nm:get("color") or e.color
        
        e:set_color(color)
    end or nil,
    set_color = function(e, color)
        e.color = color
        
        e.object:set_properties {
            textures = {"blank.png", "artifact_color_swapper_cube_"..e.color..".png"}
        }
        
        local nm = minetest.get_meta(e.object:get_pos())
        nm:set_string("color", color)
    end,
    rotate = function(e, rot)
        e.object:set_rotation(rot)
        e.rotation = rot
    end,
})

local function swapper_onload(pos)
    local m = minetest.get_meta(pos)
    if not m:contains("initialized") then
        m:set_string("initialized", "true")
        local rot = artifact.facedir_to_rotation(minetest.get_node(pos).param2)
        minetest.add_entity(pos, "artifact:color_swapper_display", minetest.serialize{rotation = rot, color = "gold"})
    end
end

artifact.register_node("color_swapper", {
    drawtype = "nodebox",
    node_box = {
        type = "fixed",
        fixed = {
            -0.5, -0.45, -0.5,
            0.5, -0.45, 0.5
        }
    },
    tiles = {"artifact_color_swapper_base.png"},
    use_texture_alpha = "clip",
    paramtype = "light",
    walkable = false,
    paramtype2 = "facedir",
    groups = {call_on_load = 1},
    light_source = 8,
    on_construct = swapper_onload,
    on_load = swapper_onload,
    on_destruct = function(pos)
        swappers[pos:to_string()].object:remove()
        swappers[pos:to_string()] = nil
    end,
    on_rotate = function(pos, node, p, click, param2)
        node.param2 = param2
        minetest.swap_node(pos, node)
        local rot = artifact.facedir_to_rotation(param2)
        swappers[pos:to_string()]:rotate(rot)
        return true
    end
})


-- MARK: Targets

local targets = {}

minetest.register_entity(":artifact:color_target_display", {
    initial_properties = {
        visual = "cube",
        mesh = "artifact_color_target.gltf",
        textures = {"artifact_color_target_base.png^artifact_color_target_gold_off.png"},
        use_texture_alpha = true,
        shaded = false,
    },
    _interact_time = 0.4,
    _can_interact = function(e, m)
        return m.character == "key" and m.color == e.color
    end,
    on_activate = function(e, data)
        local pos = e.object:get_pos()
        local node = minetest.get_node(pos)
        if not node.name:find "color_target" then
            e.object:remove()
            return
        end
        e.object:set_armor_groups{immortal = 1}
        extend(e, minetest.deserialize(data) or {})
        
        if e.rotation then
            e:rotate(e.rotation)
        else
            e:rotate(vector.zero())
        end
        e._name = ""..math.random()
                
        local nm = minetest.get_meta(e.object:get_pos())
        local color = nm:get("color") or e.color
        
        e:set_color(color)
        
        targets[e.object:get_pos():round():to_string()] = e
    end,
    on_deactivate = function(e)
        targets[e.object:get_pos():round():to_string()] = nil
    end,
    get_staticdata = function(e)
        return minetest.serialize{rotation = e.rotation, color = e.color}
    end,
    on_interact = function(e, m)
        if m.color == e.color then
            e._no_interact = true
            m:set_color(nil)
            artifact.play_sound {
                name = "artifact_color_use",
                pos = e.object:get_pos(),
                range = 8,
            }
            -- Update the entity's texture every globalstep, because that's the only way to do a texture animation.
            local progress = 0
            local delay = 0
            local dir = true
            local function update()
                if dir then
                    progress = progress +(255 *0.05)
                    if progress >= 255 then
                        dir = false
                        minetest.after(1, update)
                        local receivers = minetest.deserialize(minetest.get_meta(e.object:get_pos():round()):get("receivers") or "return nil")
                        if receivers then
                            artifact.dispatch_event(receivers, {type = "pulse"})
                        end
                        return
                    end
                else
                    progress = progress -(255 *0.05)
                    if progress < 0 then
                        e._no_interact = nil
                        local tx = "artifact_color_target_base.png^artifact_color_target_"..e.color.."_off.png"
                        e.object:set_properties {
                            textures = {tx, tx, tx, tx, tx, tx}
                        }
                        return
                    end
                end
                local tx = "artifact_color_target_base.png^artifact_color_target_"..e.color.."_off.png^(artifact_color_target_"..e.color.."_on.png^[opacity:"..progress..")"
                e.object:set_properties {
                    textures = {tx, tx, tx, tx, tx, tx}
                }
                minetest.after(0, update)
            end
            minetest.after(0, update)
        end
    end,
    -- Let us update the color live in debug mode.
    on_punch = artifact.debug and function(e)
        local nm = minetest.get_meta(e.object:get_pos())
        local color = nm:get("color") or e.color
        
        e:set_color(color)
    end or nil,
    set_color = function(e, color)
        e.color = color
        
        local tx = "artifact_color_target_base.png^artifact_color_target_"..e.color.."_off.png"
        e.object:set_properties {
            textures = {tx, tx, tx, tx, tx, tx}
        }
        
        local nm = minetest.get_meta(e.object:get_pos())
        nm:set_string("color", color)
    end,
    rotate = function(e, rot)
        e.object:set_rotation(rot)
        e.rotation = rot
    end,
})

local function target_onload(pos)
    local m = minetest.get_meta(pos)
    if not m:contains("initialized") then
        m:set_string("initialized", "true")
        local rot = artifact.facedir_to_rotation(minetest.get_node(pos).param2)
        minetest.add_entity(pos, "artifact:color_target_display", minetest.serialize{rotation = rot, color = "gold"})
    end
end

artifact.register_node("color_target", {
    drawtype = "airlike",
    tiles = {"artifact_color_target_base.png^artifact_color_target_gold_off.png"},
    use_texture_alpha = "clip",
    paramtype = "light",
    paramtype2 = "facedir",
    groups = {call_on_load = 1},
    sounds = artifact.sounds.metal,
    on_construct = target_onload,
    on_load = target_onload,
    on_destruct = function(pos)
        targets[pos:to_string()].object:remove()
        targets[pos:to_string()] = nil
    end,
    on_rotate = function(pos, node, p, click, param2)
        node.param2 = param2
        minetest.swap_node(pos, node)
        local rot = artifact.facedir_to_rotation(param2)
        targets[pos:to_string()]:rotate(rot)
        return true
    end
})
