
mesecons_automove = {}


local function put_node(pos,node)
    minetest.set_node(pos,node)
--    mesecon.on_placenode(pos,node)
end

local function put_node_activate(pos,node)
    minetest.set_node(pos,node)
    mesecon.on_placenode(pos,node)

end


local function sort_xz(p1,p2)
    if (p1.x < p2.x ) then
	return p1,p2
    elseif( p1.x > p2.x ) then
	return p2,p1
    else
	-- p1.x == p2.x
	if( p1.z < p2.z ) then
	    return p1,p2
	else
	    return p2,p1
	end
    end
    return p1,p2
end

function update_selection()
        if(mesecons_automove.pos1e ~= nil) then
                mesecons_automove.pos1e:remove()
                mesecons_automove.pos1e = nil
        end
        if(mesecons_automove.pos2e ~= nil) then
                mesecons_automove.pos2e:remove()
                mesecons_automove.pos2e = nil
        end
        
        if(mesecons_automove.pos1 ~= nil ) then
                mesecons_automove.pos1e = 
                        minetest.add_entity(mesecons_automove.pos1,"mesecons_automove:pos1")
        end
        if(mesecons_automove.pos2 ~= nil ) then
                mesecons_automove.pos2e = 
                        minetest.add_entity(mesecons_automove.pos2,"mesecons_automove:pos2")
        end
        
        
end

--[[
function update_selection()
    if( mesecons_automove.pos1 == nil ) or (mesecons_automove.pos2 == nil ) then return end	

    if( mesecons_automove.pos1 ~= nil ) and (mesecons_automove.pos1e ~= nil ) then
	mesecons_automove.pos1e:remove()
	mesecons_automove.pos1e = minetest.add_entity(mesecons_automove.pos1,"mesecons_automove:pos1")	
    end
    if( mesecons_automove.pos2 ~= nil ) and (mesecons_automove.pos2e ~= nil ) then
	mesecons_automove.pos2e:remove()
	mesecons_automove.pos2e = minetest.add_entity(mesecons_automove.pos2,"mesecons_automove:pos2")	
    end

end

]]--

function radians_to_vectors(rad)
    local pi = math.pi
    if (rad>=0) and (rad<=pi/4) or (rad<=2*pi) and (rad>=(3/2+1/4)*pi) then
	return {left = {x=-1,z=0},right = { x=1, z = 0 } } 
    elseif (rad >= 1/4*pi) and (rad <= (1/2+1/4)*pi) then
	return { left = {x = 0 ,z=-1}, right = { x = 0, z = 1 } } 	
    elseif (rad >= (1-1/4)*pi ) and (rad <= (3/2-1/4)*pi ) then 
	return { left =  { x = 1, z =0 }, right = { x = -1, z = 0 } }
    else	
	return { left = { z =1, x = 0 } , right = { z = -1 , x = 0 }} 
    end
end



local function get_front_wall(pos1,pos2,direction)
    local y = pos1.y -- p1.y == p2.y 
    local x,z
    local d= direction
    if d.x == -1 then
	local m = math.min(pos1.x,pos2.x)-1  -- aoutside the selection
	return { 
	    pos1 = { x = m, y = y, z = pos1.z},
	    pos2 = { x = m, y = y, z = pos2.z},
	    }
    elseif d.x == 1 then
	local m = math.max(pos1.x,pos2.x)+1  -- aoutside the selection
	return { 
	    pos1 = { x = m, y = y, z = pos1.z},
	    pos2 = { x = m, y = y, z = pos2.z},
	    }

    elseif d.z == -1 then
	local m = math.min(pos1.z,pos2.z)-1  -- aoutside the selection
	return { 
	    pos1 = { x = pos1.x, y = y, z = m},
	    pos2 = { x = pos2.x, y = y, z = m},
	    }

    else
        --if d.z == 1 then
	local m = math.max(pos1.z,pos2.z)+1  -- aoutside the selection
	return { 
	    pos1 = { x = pos1.x, y = y, z = m},
	    pos2 = { x = pos2.x, y = y, z = m},
	    }
    end

end

local function get_back_wall(p1,p2,direction)
    local pos1,pos2 = sort_xz(p1,p2)
    local y = pos1.y -- p1.y == p2.y 
    local x,z
    local d= direction
    if d.x == 1 then
	local m = math.min(pos1.x,pos2.x)  -- aoutside the selection
	return { 
	    pos1 = { x = m, y = y, z = pos1.z},
	    pos2 = { x = m, y = y, z = pos2.z},
	    }
    elseif d.x == -1 then
	local m = math.max(pos1.x,pos2.x)  -- aoutside the selection
	return { 
	    pos1 = { x = m, y = y, z = pos1.z},
	    pos2 = { x = m, y = y, z = pos2.z},
	    }

    elseif d.z == 1 then
	local m = math.min(pos1.z,pos2.z)  -- aoutside the selection
	return { 
	    pos1 = { x = pos1.x, y = y, z = m},
	    pos2 = { x = pos2.x, y = y, z = m},
	    }

    else
        --if d.z == 1 then
	local m = math.max(pos1.z,pos2.z)  -- aoutside the selection
	return { 
	    pos1 = { x = pos1.x, y = y, z = m},
	    pos2 = { x = pos2.x, y = y, z = m},
	    }
    end
    
end



local function have_output(pos,direction)
    local neight_pos = { x = pos.x -direction.x, y = pos.y, z = pos.z - direction.z } 
    local neight_pin = get_pins_sticky(neight_pos)

    local pin = get_pins_sticky(pos)


    -- check if connected with each other
    if(direction.x == 1 ) then
	if not (( pin.l == 1 ) and (neight_pin.r == 1 ) ) then return false end	
    elseif( direction.x == -1 ) then
	if not ( (pin.r == 1) and (neight_pin.l == 1) ) then return false end
    elseif( direction.z == 1 ) then
	if not (( pin.d == 1 ) and (neight_pin.u == 1 ) ) then return false end
    else
	if not ((pin.u == 1) and (neight_pin.d == 1 )) then return false end
    end

print("debug.pin="..dump(pos))
    if( pin.u == 1 ) and (direction.z == -1 ) then return true end
    if( pin.d == 1 ) and (direction.z == 1 ) then return true end
    if( pin.l == 1 ) and (direction.x == 1 ) then return true end
    if( pin.r == 1 ) and (direction.x == -1 ) then return true end

    return false
end


local function get_outputs(wall,direction)
    local pos1 = wall.pos1
    local pos2 = wall.pos2

    local list = {}
    if pos1.x == pos2.x then
	local f = math.min(pos1.z, pos2.z)
	local t = math.max(pos1.z, pos2.z)
	for i=f,t do
	    local posi = { y = pos1.y, x = pos1.x , z = i }
	    if( have_output( posi , direction ) == true ) then
		table.insert(list,posi)
	    end
	end
    else
	-- pos1.z == pos2.z
	local f = math.min(pos1.x, pos2.x)
	local t = math.max(pos1.x, pos2.x)
	for i=f,t do
	    local posi = { y = pos1.y, z = pos1.z , x = i }
	    if( have_output( posi , direction ) == true ) then
		table.insert(list,posi)
	    end
	end

    end

print("DEBUG.list = "..dump(list))
    return list
end


-- callback fun(pos)
local function foreach_wall(wall,fun)
    local pos1 = wall.pos1
    local pos2 = wall.pos2

    if pos1.x == pos2.x then
	local f = math.min(pos1.z, pos2.z)
	local t = math.max(pos1.z, pos2.z)
	for i=f,t do
	    local posi = { y = pos1.y, x = pos1.x , z = i }
	    fun(posi)
	end
    else
	-- pos1.z == pos2.z
	local f = math.min(pos1.x, pos2.x)
	local t = math.max(pos1.x, pos2.x)
	for i=f,t do
	    local posi = { y = pos1.y, z = pos1.z , x = i }
	    fun(posi)
	    
	end

    end
end


local function move_single_node(pos,direction)
    local node = minetest.get_node(pos)
    minetest.set_node(pos, { name = "air" } )
    local new_pos = { x = pos.x + direction.x,y =  pos.y ,z =  pos.z + direction.z }
    put_node(new_pos,node)
end


local function move_nodes(direction)
    local pos1 = mesecons_automove.pos1
    local pos2 = mesecons_automove.pos2


    -- needed later
    local back_wall = get_back_wall(pos1,pos2,direction)
    local outputs = get_outputs(back_wall,direction)



    -- removes front wall
    local wall = get_front_wall(pos1,pos2,direction)
    foreach_wall(wall, function(pos) minetest.set_node(pos,{name="air"}) end )

    -- moves each "colum" from the front to end
    local xf, xt, xd , zf, zt, zd
    
    if( direction.x == 1 ) then 
	xt = math.min(pos1.x, pos2.x)
	xf = math.max(pos1.x, pos2.x)
	zf = math.min(pos1.z, pos2.z)
	zt = math.max(pos1.z, pos2.z)
	xd = -1
	zd = 1
    elseif(direction.x == -1 ) then
	xf = math.min(pos1.x, pos2.x)
	xt = math.max(pos1.x, pos2.x)
	zf = math.min(pos1.z, pos2.z)
	zt = math.max(pos1.z, pos2.z)
	xd = 1
	zd = 1

    elseif(direction.z == 1 ) then
	xf = math.min(pos1.x, pos2.x)
	xt = math.max(pos1.x, pos2.x)
	zt = math.min(pos1.z, pos2.z)
	zf = math.max(pos1.z, pos2.z)
	xd = 1
	zd = -1

    else -- direction.z == -1
	xf = math.min(pos1.x, pos2.x)
	xt = math.max(pos1.x, pos2.x)
	zf = math.min(pos1.z, pos2.z)
	zt = math.max(pos1.z, pos2.z)
	xd = 1
	zd = 1
    end

    for ix = xf,xt,xd do
	for iz = zf,zt,zd do
--	    print("move("..ix..","..iz..")")
	    local current = { x = ix, y=pos1.y , z = iz } 
	    move_single_node(current,direction)
	end
    end


    -- adding wires at the end

print("debug:  back_wall =" ..dump(back_wall) .. ",   outputs = ".. dump(outputs))
    for _,p in ipairs(outputs) do
	if( direction.z == 0 ) then
	    -- x direction
	    put_node_activate(p,{name = "mesecons_insulated:insulated_off", param2 = 0 } )
	else
	    -- z direction
	    put_node_activate(p,{name = "mesecons_insulated:insulated_off", param2 = 1 } )
	end
    end    
    
    -- activate front wall
    --TODO: refresh new value of wires 

end







local function add_direction_to_pos(pos,direction)
    return {x =  pos.x + direction.x, y=  pos.y, z = pos.z + direction.z }
end

local function can_move_node(pos,direction)
    local node =  minetest.get_node(pos)
    local name = node.name
    local param2= node.param2

    if( name == "air" ) then 
	return true 
    end
    if( name == "mesecons_insulated:insulated_off" ) or ( name == "mesecons_insulated:insulated_on" ) then
	if (param2==0) or (param2==2) then	
	    if( direction.z==0) then return true else return false end
	elseif (param2==1) or (param2==3) then
	    if( direction.x == 0) then return true else return false end
	end
    else
	return false
    end
end

local function check_if_can_move(direction)
    local pos1 = mesecons_automove.pos1
    local pos2 = mesecons_automove.pos2
    
    pos1,pos2 = sort_xz(pos1,pos2)    
    local wall = get_front_wall(pos1,pos2,direction)

    pos1 = wall.pos1
    pos2 = wall.pos2
    
    local v 
    if( pos1.x == pos2.x ) then	
	if( pos1.z < pos2.z ) then
	    v = {z=1,x=0}
	else
	    v = {z=-1,x=0}	
	end
    else
	-- pos1.z == pos2.z 
	if( pos1.x < pos2.x ) then
		v = {z=0,x=1}
	else
	    v ={ z=0,x=-1}
	end
    end
    
    local current = pos1
    while( eq_pos(current,pos2) == false ) do 
	if( can_move_node(current,direction) == false ) then 	
	    return false
	end
	current.x = current.x + v.x 
	current.z = current.z + v.z
    end
    -- do last node
    if( can_move_node(current,direction) == false ) then 	
        return false
    end
    
    return true    
end


local function move_selection_left(rad)
    local v = radians_to_vectors(rad)
    local pos1 = mesecons_automove.pos1 
    local pos2 = mesecons_automove.pos2
    if( pos1 == nil ) then return end
    if( pos2 == nil ) then return end



    if( check_if_can_move(v.left) == false ) then return end
    move_nodes(v.left)

    pos1.x = pos1.x + v.left.x
    pos1.z = pos1.z + v.left.z 

    pos2.x = pos2.x + v.left.x
    pos2.z = pos2.z + v.left.z 


    mesecons_automove.pos1 = pos1
    mesecons_automove.pos2 = pos2
    update_selection()


end

local function move_selection_right(rad)
    local v = radians_to_vectors(rad)

    local pos1 = mesecons_automove.pos1 
    local pos2 = mesecons_automove.pos2
    if( pos1 == nil ) then return end
    if( pos2 == nil ) then return end


    if( check_if_can_move(v.right) == false ) then return end
    move_nodes(v.right)


    pos1.x = pos1.x + v.right.x
    pos1.z = pos1.z + v.right.z 

    pos2.x = pos2.x + v.right.x
    pos2.z = pos2.z + v.right.z 



    mesecons_automove.pos1 = pos1
    mesecons_automove.pos2 = pos2
    update_selection()



end


minetest.register_entity(":mesecons_automove:pos1", {
        initial_properties = {
                visual = "cube",
                visual_size = {x=1.1, y=1.1, },
                textures = {"worldedit_pos1.png", "worldedit_pos1.png",
                        "worldedit_pos1.png", "worldedit_pos1.png",
                        "worldedit_pos1.png", "worldedit_pos1.png"},
                collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
                physical = false,
                static_save = false,
        },
        on_activate = function(self, staticdata, dtime_s)
	    
        end,
        on_punch = function(self, hitter)
                self.object:remove()
		mesecons_automove.pos1e = nil
		mesecons_automove.pos1 = nil
	        mesecons_automove.pos2 = nil
		if( mesecons_automove.pos2e ~= nil ) then 
		    mesecons_automove.pos2e:remove()
		    mesecons_automove.pos2e = nil
		end
		print("debug.self:remove()")
        end,
        on_blast = function(self, damage)
                return false, false, {} -- don't damage or knockback
        end,
})

minetest.register_entity(":mesecons_automove:pos2", {
        initial_properties = {
                visual = "cube",
                visual_size = {x=1.1, y=1.1},
                textures = {"worldedit_pos2.png", "worldedit_pos2.png",
                        "worldedit_pos2.png", "worldedit_pos2.png",
                        "worldedit_pos2.png", "worldedit_pos2.png"},
                collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
                physical = false,
                static_save = false,
        },
        on_activate = function(self, staticdata, dtime_s)
        end,
        on_punch = function(self, hitter)
                self.object:remove()
		mesecons_automove.pos2e = nil
		mesecons_automove.pos2 = nil
	        mesecons_automove.pos1 = nil
		if( mesecons_automove.pos1e ~= nil) then
		    mesecons_automove.pos1e:remove()

		end
        end,
        on_blast = function(self, damage)
                return false, false, {} -- don't damage or knockback
        end,
})

minetest.register_entity(":mesecons_automove:move_region", {
        initial_properties = {
                visual = "upright_sprite",
                textures = {"worldedit_cube.png"},
                visual_size = {x=10, y=1.1},
                physical = false,
                static_save = false,
        },
        on_activate = function(self, staticdata, dtime_s)
        end,
        on_punch = function(self, hitter)
	    self.object:remove()
        end,
        on_blast = function(self, damage)
                return false, false, {} -- don't damage or knockback
        end,
})

--minetest.register_craftitem("mesecons_autowire:move", {
minetest.register_tool("mesecons_autowire:select", {
    description = "Tool Autoselect",
    inventory_image = "tool_autoselect.png",
    stack_max = 1,
    groups = { tool =1  },


   on_use = function(itemstack, user, pointed_thing) 
	if pointed_thing == nil then return end
        if pointed_thing.type == "nothing"  then 
	    --unselect
            mesecons_automove.pos1 = nil
            mesecons_automove.pos2 = nil
            update_selection()

	    return 	
	elseif pointed_thing.type == "node" then
	    --select pos1
            local node_pos = pointed_thing.under
	    if( mesecons_automove.pos1e ~= nil ) then
		    mesecons_automove.pos1e:remove()
	    end

	    if( is_circuit_element(node_pos) ) then
        	    mesecons_automove.pos1 = node_pos
        	else
		    mesecons_automove.pos1 = pointed_thing.above
    	    end
	    mesecons_automove.pos1e = minetest.add_entity(mesecons_automove.pos1,"mesecons_automove:pos1")

	end
    end,
    on_place = function(itemstack, user, pointed_thing)
        if  pointed_thing == nil then return end
        if pointed_thing.type == "node"  then 
            local node_pos = pointed_thing.under

	    if( mesecons_automove.pos2e ~= nil ) then
		    mesecons_automove.pos2e:remove()
	    end


	    if( is_circuit_element(node_pos) ) then
    		mesecons_automove.pos2 = node_pos
            else
	        mesecons_automove.pos2 = pointed_thing.above
    	    end
	    mesecons_automove.pos2e = minetest.add_entity(mesecons_automove.pos2,"mesecons_automove:pos2")
        end
    end,

    on_secondary_use = function(itemstack, user, pointed_thing)
	if pointed_thing == nil then return end
	if pointed_thing.type == "nothing" then
	    --move
	end
    end
    

})

minetest.register_tool("mesecons_autowire:move", {
    description = "Tool Automove",
    inventory_image = "tool_automove.png",
    stack_max = 1,
    groups = { tool =1  },


   on_use = function(itemstack, user, pointed_thing) 
	    if mesecons_automove.pos1 == nil then return end
	    if mesecons_automove.pos2 == nil then return end
	    move_selection_left(user:get_look_horizontal())


    end,
    on_place = function(itemstack, user, pointed_thing)
	    if mesecons_automove.pos1 == nil then return end
	    if mesecons_automove.pos2 == nil then return end
	    move_selection_right(user:get_look_horizontal())

    end,

    on_secondary_use = function(itemstack, user, pointed_thing)
	    if mesecons_automove.pos1 == nil then return end
	    if mesecons_automove.pos2 == nil then return end
	    move_selection_right(user:get_look_horizontal())
	
    end
    

})

