-- support for MT game translation.
local S = default.get_translator

--[[ 
current = pos left bottom
posh = pos right bottom
posv = pos left top
depends where you look at
]]--

local function standarise_corners(pos1,pos2,direction)
        --assert(pos1.y == pos2.y) TODO: how to solve the problem of selectin not flat region

        local x1,x2,z1,z2,y
        y = pos1.y

        x1 = pos1.x
        x2 = pos2.x
        z1 = pos1.z
        z2 = pos2.z

        x1,x2 = math.min(x1,x2), math.max(x1,x2)
        z1,z2 = math.min(z1,z2), math.max(z1,z2)

        local p1,p2,p3,p4

        p1 = { x=x1,y=y,z=z1 }
        p2 = { x=x2,y=y,z=z1 }
        p3 = { x=x1,y=y,z=z2 }
        p4 = { x=x2,y=y,z=z2 }

        if( direction.x == -1 ) then
                return p2,p4,p1
        elseif( direction.x == 1 ) then
                return p3,p1,p4
        elseif( direction.z == -1 ) then
                return p4,p3,p2
        else
                return p1,p2,p3
        end
end



local function iter_vectors(direction)
        local dx = direction.x
        local dz = direction.z
        
        if( direction.z == 1 ) then
                return {x=1,z=0},{x=0,z=1}
        elseif( direction.z == -1 ) then
                return {x=-1,z=0},{x=0,z=-1}
        elseif( direction.x == -1 ) then
                return {x=0,z=1},{x=-1,z=0}
        else
                return {x=0,z=-1},{x=1,z=0}
        end
end


function check_selection(pos1,pos2)
        if( pos1 == nil ) then return false end
        if( pos2 == nil ) then return false end
        return true
end

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




local function store_nodes(p1,p2,direction)
        
        local pos1,pos2 = p1,p2
        local tab = {}
        
        if( check_selection(pos1,pos2) == false ) then 
                return nil
        end
        local corner, maxh, maxv  = standarise_corners(pos1,pos2,direction)
        local inch,incv = iter_vectors(direction)
        
        
        local i,k = 1,1
        
        local cur,curv,curh = corner,corner,corner
        
        while true do                
                i = 1
                tab[k] = {}                                 
                while true do
                        tab[k][i] = minetest.get_node(cur)
                        if( eq_pos(curh,maxh) ) then
                                break
                        end
                        curh = add_direct_to_pos(curh,inch)  
                        cur = add_direct_to_pos(cur, inch)
                        i = i + 1
                end
                
                if( eq_pos(curv,maxv) ) then 
                        break
                end
                curv = add_direct_to_pos(curv,incv)       
                cur = curv
                curh = corner
                k = k +1
        end
        
        --print("["..i.."]["..k.."]="..name)
        --print("table = ".. dump(tab))
        return tab

end

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


local function store_nodes_from_selection(rad)
        
        local direction = radians_to_direction_looking_forward(rad)
        return store_nodes(mesecons_automove.pos1, mesecons_automove.pos2,direction)
end



local function circuit_on_place(itemstack, user, pointed_thing)
        print("debug:on_plalce_circuit")
end



local max_text_size = 10000
local max_title_size = 80
local short_title_size = 35


minetest.register_on_player_receive_fields(function(player, formname, fields)
     if formname ~= "mesecons_autowire:circuit" then return end   
     
     if( fields.save) then
        local stack = player:get_wielded_item()  
        local inv = player:get_inventory()
        local new_stack = nil
        local meta = stack:get_meta()        
        local data = meta:to_table().fields
        
        if data == nil then data = {} end
        data.title = fields.title or ""
        
        
        
        -- if no nodes then save nodes
        local nodes = minetest.deserialize(data.nodes)
        
        --if not data.nodes then
        if nodes == nil then
                local rad = player:get_look_horizontal()
                local tab = store_nodes_from_selection(rad)
                --print("tab="..dump(tab))
                if( tab ) then
                        data.nodes = minetest.serialize(tab)
                        data.rad = rad
                else
                        return
                end
                                
        end
                     
             
            -- print("debug.af="..dump(data))

        if( stack:get_name() == "mesecons_autowire:circuit_full" ) then
                
        elseif ( stack:get_name() == "mesecons_autowire:circuit_empty" ) then
                local count = stack:get_count()        
                if( count <= 0 ) then return end
                print("debug.count="..count)
                if count == 1 then      
                        --inv:remove_item("main",stack)
                        stack:set_name("mesecons_autowire:circuit_full")
                else
                        stack:set_count(count - 1)
                        print("debug.count*="..stack:get_count() )
                        new_stack = ItemStack("mesecons_autowire:circuit_full")
                end
        else
        end
        
        
        
        if not fields.text then fields.text = "" end
        if not fields.title then fields.title = "" end
        
        data.title = fields.title:sub(1, max_title_size)
        local short_title = data.title
        -- Don't bother triming the title if the trailing dots would make it longer
        if #short_title > short_title_size + 3 then
                short_title = short_title:sub(1, short_title_size) .. "..."
        end     
        data.description = S(short_title)
        data.text = fields.text:sub(1, max_text_size)
        data.text = data.text:gsub("\r\n", "\n"):gsub("\r", "\n")                    
        
        
        if new_stack then
                
                new_stack:get_meta():from_table({ fields = data})
                
                if inv:room_for_item("main", new_stack) then
                        inv:add_item("main", new_stack)
                else
                        minetest.add_item(player:get_pos(), new_stack)
                end
        else
                print("debug.new_stack==nil")
                stack:get_meta():from_table( {fields = data } )
        end
        
        
        
        --debug
        
     
        player:set_wielded_item(stack)
        
     end
end)



local function direction_to_number(direction)
        if( direction.z == 1 ) then
                return 1
        elseif( direction.x == 1 ) then
                return 2
        elseif(direction.z == -1 )then
                return 3
        else
                return 4
        end
        
end


local rotate_exceptions = {
        "mesecons_extrawires:crossover_off",
        "mesecons_extrawires:crossover_on",
        "mesecons_morewires:xjunction_on",
        "mesecons_morewires:xjunction_off"
}

local function is_exception(node)
        for _,v in ipairs(rotate_exceptions) do
                if( v == node.name ) then
                        return true
                end
        end
        return false
end

local function node_to_image(node)
        if node == nil then
                return "unknown.png"
        end
        
        local name = node.name
        local param2 = node.param2 or 0
        
        
        if( name == "air" ) then
                return "empty.png"
        end
        
        
        if      (name == "mesecons_insulated:insulated_off") or 
                (name == "mesecons_insulated:insulated_on" ) then
                if( param2 % 2 == 0 ) then
                        return "wireh.png"
                else
                        return "wirev.png"
                end
        end
        
        if (name == "mesecons_extrawires:crossover_off") or 
        (name == "mesecons_extrawires:crossover_on") or
        (name == "mesecons_extrawires:crossover_01") or 
        (name == "mesecons_extrawires:crossover_10" ) then
                return "corssover.png"
        end
        
        if (name == "mesecons_extrawires:corner_off" ) or 
                (name == "mesecons_extrawires:corner_on" ) then
                        
                return "corner"..param2 .. ".png"
        end
        
        if (name == "mesecons_extrawires:tjunction_off" ) or 
           (name == "mesecons_extrawires:tjunction_on" ) then
                   
                return "tjunction"..param2 .. ".png"
        end
        
        if ( name == "mesecons_morewires:xjunction_off" ) or 
        ( name == "mesecons_morewires:xjunction_on" ) then
                return "xjunction.png"
        end
        
        
        if( name == "mesecons_regs:flipflop_off" ) or ( name == "mesecons_regs:flipflop_on") then
                return "ff"..param2..".png"
        end
        if( name == "mesecons_regs:latch_off" ) or ( name == "mesecons_regs:latch_on") then
                return "latch"..param2..".png"
        end
        
        
        if( name == "mesecons_gates:and_off" ) or ( name =="mesecons_gates:and_on" )then
                return "and".. param2 .. ".png"
        end
        if( name == "mesecons_gates:nand_off" ) or ( name =="mesecons_gates:nand_on" )then
                return "nand".. param2 .. ".png"
        end
        
        if( name == "mesecons_gates:nor_off" ) or ( name =="mesecons_gates:nor_on" )then
                return "nor".. param2 .. ".png"
        end
        if( name == "mesecons_gates:or_off" ) or ( name =="mesecons_gates:or_on" )then
                return "or".. param2 .. ".png"
        end
        if( name == "mesecons_gates:xor_off" ) or ( name =="mesecons_gates:xor_on" )then
                return "xor".. param2 .. ".png"
        end
        
        
        if( name == "mesecons_gates:diode_off" ) or ( name =="mesecons_gates:diode_on" )then
                return "diode".. param2 .. ".png"
        end
        
       if( name == "mesecons_gates:not_off" ) or ( name =="mesecons_gates:not_on" )then
                return "not".. param2 .. ".png"
        end    
        
        
        
        return "unknown.png"
                
end


local function generate_spec(starting,nodes)
        if nodes == nil then
                return ""
        end
        
        local mv = #nodes
        local mh = #nodes[1]
        
        
        
        
        local str = ""
        local x,z
        
        for x = 1,mh do
                for z = 1,mv do
                      local img = node_to_image(nodes[z][x])
                      
                      --str = str .. "image[" .. 0.5*x  .. "," .. 0.5*(mv-z+1) .. 
                      str = str .. "image[" .. x +starting .. "," .. (mv-z+1) .. 
                      ";1,1;"..img.."]"
                      --";0.5,0.5;"..img.."]"
                end
        end
        return str
end




local function special_case(node,rotate)
        local new_node = node
        local applied = false
        if( node.name == "mesecons_extrawires:crossover_10") and (rotate % 2 == 1 )  then 
                new_node.name = "mesecons_extrawires:crossover_01"
                new_node.param2 = 0
                return new_node
        elseif (node.name == "mesecons_extrawires:crossover_10") and (rotate % 2 == 0 )  then 
                new_node.name = "mesecons_extrawires:crossover_10"
                new_node.param2 = 0                
                return new_node
        elseif( node.name == "mesecons_extrawires:crossover_01" and (rotate % 2 == 1) ) then 
                new_node.name = "mesecons_extrawires:crossover_10"
                new_node.param2 = 0
                return new_node
        elseif ( node.name == "mesecons_extrawires:crossover_01" and (rotate % 2 == 0 ) ) then 
                new_node.name = "mesecons_extrawires:crossover_01"
                new_node.param2 = 0                
                return new_node
        end
        
        return nil  
end
local function rotate_node(node, saved_direction,direction)
        if( is_exception(node) ) then return node end
        
        local values = 
        {
                {0,1,2,3},
                {3,0,1,2},
                {2,3,0,1},
                {1,2,3,0}
        }
        
        local rotate = 
                values[direction_to_number(saved_direction)][direction_to_number(direction)]
        
        local new_node = special_case(node,rotate)
        if( new_node ~= nil ) then return new_node end
        
        
        new_node = node
        new_node.param2 = (node.param2+rotate)%4
        return new_node
end

local function rotate_nodes_for_window(nodes, oryginal)
        if nodes == nil then return nil end
        local mv = #nodes
        local mh = #nodes[1]
        local dir = {x=0,z=1}
        
        local tab = {}
        for k = 1,mv do
                tab[k] = {}
                for i = 1,mh do
                        tab[k][i] = rotate_node(nodes[k][i],oryginal,dir)
                end
        end
        return tab
        
end

function is_gate(node)
        local gates = {
                "mesecons_gates:and",
                "mesecons_gates:or",
                "mesecons_gates:xor",
                "mesecons_gates:nand",
                "mesecons_gates:nor",
                "mesecons_gates:not",
                "mesecons_gates:diode",
                "mesecons_gates3:and3",
                "mesecons_gates3:or3",
                "mesecons_gates3:nor3",
                "mesecons_gates3:nand3",
                "mesecons_regs:flipflop",
                "mesecons_regs:latch"
        }
        
        local gates_with_states = {} 
        for _,v in ipairs(gates) do
                table.insert(gates_with_states,v.."_on")
                table.insert(gates_with_states,v.."_off")
        end
        
        
        
        
        for _,v in ipairs(gates_with_states) do
                if node.name == v then 
                        print("found.gate="..v)
                        return true 
                end
        end
        
       
        return false
                
        
end


local function count_stats(tab)
        if tab == nil then return 0 end
        local block, wire, gate = 0,0,0
        
        local h,v = #tab[1], #tab
        for i = 1,h do
                for k = 1,v do
                        if tab[k][i].name ~= "air" then
                                block = block + 1
                        end
                        if is_wire_node(tab[k][i]) then
                                wire = wire + 1
                        end
                        if is_gate(tab[k][i]) then
                                gate = gate + 1
                        end
                        
                                                
                        
                        
                end
        end
        return block, wire, gate
end


local function show_dialog(itemstack,user,pointed_thing)
        local player_name = user:get_player_name()
        local formspec
        local esc = minetest.formspec_escape
        
        
        local meta = itemstack:get_meta()        
        local data = meta:to_table().fields        
        local title, text = data.title or "", data.text or ""
        
        
        
        local nodes_string = "empty"
        local nodes = minetest.deserialize(data.nodes)
        local hight = 0
        local width = 0
        if( nodes ) then
                -- show nodes
                nodes_string  = "size = (".. #nodes[1] .. ","..#nodes .. ")"
                hight = #nodes        
                width = #nodes[1]
        end
        
        
        if( nodes == nil ) and 
          ( check_selection(mesecons_automove.pos1,mesecons_automove.pos2) == false ) then
                  return
          end
          
        if nodes == nil then
                local rad = user:get_look_horizontal()
                local tab = store_nodes_from_selection(rad)
                if tab == nil then return end
                nodes = tab
                data.rad = rad
                hight = #nodes
                width = #nodes[1]
        end
        
        
        
        
                
        
        local size_w = 45
        size_w = width + 10
        if( size_w < 20 ) then size_w = 20 end
        if( size_w > 45 ) then size_w = 45 end
        local size_scroll = size_w - 10
        
        local starting_point = math.max(0, (size_scroll/2) - (width/2))
        
        
        local block,wire,gate = count_stats(nodes)
        local stats = "size : " .. width .. "x"..hight .. "(=".. width * hight .. ")\n" ..
                        "wires : " .. wire .. "\n" ..
                        "gates : " .. gate .. "\n" ..
                        "other : ".. block - wire - gate .. "\n" .. 
                        "blocks: " .. block
                        
        
        local circuitspec = 
                generate_spec(starting_point,
                        rotate_nodes_for_window(
                                nodes,
                                radians_to_direction_looking_forward(
                                        tonumber(data.rad))))
     
        formspec = 
                "formspec_version[3]"..
                --"size[45,25]" ..
                "size[".. size_w .. ",25]"..
                "field[0.5,1;8,1;title;"..S("Name")..";"..S(title).."]"..                
                "textarea[0.5,3;8,15;text;" .. esc(S("Description:")) .. ";" ..
                        esc(S(text)) .. "]" ..
                "button_exit[2.5,18;3,1;save;" .. esc(S("Save")) .. "]" ..                
                "textarea[0.5,19;8,5;;;" ..
                        esc(S(stats)) .. "]" ..
                --"label[10,0.5;"..nodes_string.."]"..
                --"container[8,0.5]"..
                "scrollbaroptions[min=0;max=".. hight -15 .. ";smallstep=2;largestep=10;arrows=show]"..
                
                --"scrollbar[44,0.5;0.6,24;vertical;scr0;0]"..
                "scrollbar[".. size_w - 1 .. ",0.5;0.6,24;vertical;scr0;0]"..
                
                --"scroll_container[8,0.5;36,23;scr0;vertical;1]"..
                "scroll_container[8,0.5;".. size_w -10 .. ",23;scr0;vertical;1]"..
                circuitspec ..
               
                "scroll_container_end[]"..
                --"container_end[]"..
                
                ""
        minetest.show_formspec(player_name, "mesecons_autowire:circuit", formspec)
        return itemstack         
end



local function make_selection(nodes,pos,rad)
        
        if nodes == nil then return end
        --if check_selection(mesecons_automove.pos1,mesecons_automove.pos2) == false then return end
        
        local vsize = #nodes
        local hsize = #nodes[1]
        
       -- print("dump"..dump(nodes) .. "====" .. dump(pos) .. "===" .. vsize .. "=" .. hsize  )
        
        local direction = radians_to_direction_looking_forward(rad)
        
        mesecons_automove.pos1 = pos
        
        local dx,dz = 0,0
        
        --print("dumpsirection="..dump(direction))
        hsize = hsize -1
        vsize = vsize -1
        if direction.z == 1 then
                dx,dz = hsize,vsize
        elseif direction.z == -1 then
                dx,dz = -hsize,-vsize
        elseif direction.x == 1 then
                dx,dz = vsize,-hsize
        else
                --dirextion.x == -1
                dx,dz = -vsize,hsize
        end
                
        
        
        
        mesecons_automove.pos2 = { x=pos.x + dx, y=pos.y, z = pos.z+dz } 
        mesecons_automove.rad = rad
        
        --print("debug.all="..dump(mesecons_automove))
        update_selection()
        
end




local function paste_nodes(nodes,saved_direction,direction)
        local pos1,pos2 = mesecons_automove.pos1,mesecons_automove.pos2
        
        if( check_selection(pos1,pos2) == false ) then 
                return nil
        end
        local corner, maxh, maxv  = standarise_corners(pos1,pos2,direction)
        local inch,incv = iter_vectors(direction)
        
        local tab = nodes
        if tab == nil then return end
        local maxi = #nodes[1]
        local maxk = #nodes
       
        local i,k = 1,1
        
        local cur,curv,curh = corner,corner,corner
        
        while true do                
                i = 1                            
                while true do
                        --tab[k][i] = minetest.get_node(cur)
                        if( i<=maxi) and (k<=maxk) then
                                local n = rotate_node(tab[k][i],saved_direction,direction)                        
                                minetest.set_node(cur,n)
                        end
                        
                        
                        
                        
                        
                        if( eq_pos(curh,maxh) ) then
                                break
                        end
                        curh = add_direct_to_pos(curh,inch)  
                        cur = add_direct_to_pos(cur, inch)
                        i = i + 1
                end
                
                if( eq_pos(curv,maxv) ) then 
                        break
                end
                curv = add_direct_to_pos(curv,incv)       
                cur = curv
                curh = corner
                k = k +1
        end       
        
end


local function on_use_circuit(itemstack, user, pointed_thing)
        if( pointed_thing.type == "node" ) then
                -- select region
                local nodes = minetest.deserialize(itemstack:get_meta():to_table().fields.nodes)
                
                local sel_pos
                if( is_circuit_element(pointed_thing.under) ) then
                        sel_pos = pointed_thing.under
                else
                        sel_pos = pointed_thing.above
                end
                
                make_selection(nodes,sel_pos,user:get_look_horizontal())
        else
                -- show dialog
                show_dialog(itemstack,user,pointed_thing);
        end      
end

local function on_place_circuit(itemstack, user, pointed_thing)
        if pointed_thing.type == "node" then
                -- paste
                
        end
        
        if( mesecons_automove.rad == nil ) then return end
        local data = itemstack:get_meta():to_table()
        local nodes = minetest.deserialize(data.fields.nodes)
        local rad = tonumber( data.fields.rad )        
        paste_nodes(nodes,radians_to_direction_looking_forward(rad), radians_to_direction_looking_forward(mesecons_automove.rad))        
end


minetest.register_craftitem("mesecons_autowire:circuit_empty", {
                description = S("Circuit empty"),
                inventory_image = "circuit_empty.png",                                
                
                on_use = on_use_circuit
                
        })

minetest.register_craftitem("mesecons_autowire:circuit_full", {
                description = S("Circuit saved"),
                inventory_image = "circuit_full.png",                
                groups = {not_in_creative_inventory = 1},                
                stack_max = 1,
                on_use = on_use_circuit,
                on_place = on_place_circuit,
                on_secondary_use = on_place_circuit
               
                
        })
