gates= {};
gates.max_distance = 32;
gates.max_volume = 2000;

local savedir = minetest.get_worldpath().."/gates/"

local template = [[
	size[8,6;]
	label[0.1,0.0;Addis Gates Mod: Gate Controller]
	box[0.1,0.6;7.5,0.05;#FFFFFF]
	box[0.1,5;7.5,0.05;#FFFFFF]
]]

if default and default.gui_bg then
	template = template..default.gui_bg;
end

if default and default.gui_bg_img then
	template = template..default.gui_bg_img;
end

if default and default.gui_slots then
	template = template..default.gui_slots;
end

local form_page0 = template .. [[
	label[0.1,1;This Assistant will help you to setup the Gate Controller.

Notice: Sometimes you have to leave this formspec to do
some action and then came back for the next steps.

The words »opened« and »closed« only means that there
are two states (see something on »opened« and then see
something different on »closed«).

To continue click Next]

	button[4.9,5.6;3,0;next;Next]
]]

local form_page1 = template .. [[
	label[0.1,0.7;First, you need to set the area within the gate is acting.
Attention: Please think about that the area have big enough to
contain the buildings for the »closed« and the »opened« gate.]

	field[0.4,3;2,0;pos1;Position 1 : (x,y,z);%s]
	button[2,2.7;5,0;cb_read1;Read pos1 of your last punched node]
	tooltip[cb_read1;Leave formspec, punch something and came back]

	field[0.4,4;2,0;pos2;Position 2 : (x,y,z);%s]
	button[2,3.7;5,0;cb_read2;Read pos2 of your last punched node]
	tooltip[cb_read2;Leave formspec, punch something different and came back]

	button[0.1,5.6;3,0;prev;Back] button[4.9,5.6;3,0;next;Next]
]]

local form_page2 = template ..[[
	label[0.1,1;Now its time for building something.

Do you want to build the »opened« or the »closed« Gate first?]

	button[0.5,3;7,0;build_opend;I want to build the »opened« gate first]

	button[0.5,4;7,0;build_closed;I will start with the »closed« one]

	button[0.1,5.6;3,0;prev;Back]
]]

local form_page2_opend = template ..[[
	label[0.1,1;Now you have to build a gate which is »opened«.

How to: First leave this formspec with ESC-Key and build
something in the previous marked area. After that came back
here for next steps.

All build and back again here? Then click Next]

	button[0.1,5.6;3,0;prev;Back] button[4.9,5.6;3,0;opend_finisched;Next]
]]

local form_page2_closed = template ..[[
	label[0.1,1;OK, The next step is to build the gate which is »closed«.

How to: First leave this formspec with ESC-Key and chance
something in the previous marked area. After that came back
here for next steps.

All chances done and back here again? Then click Next]

	button[0.1,5.6;3,0;prev;Back] button[4.9,5.6;3,0;closed_finisched;Next]
]]
local form_page3 = template .. [[
	label[0.1,1;Congratulation! The setup is complete, and the controller is ready.
Currently the gate is »%s«.

Click on the button toggle or punch this node to switch.
If mesecons installed you can also use mesecons.]

	button[0.5,4;7,0;toggle;Toggle (switch between »open« and »closed« gate)]

	button[0.1,5.6;3,0;setup;Re-Start Setup]
]]

if minetest.mkdir then
	minetest.mkdir(savedir)
else
	os.execute('mkdir "'..savedir..'"')
end

local function schematic_exists(pos,num)
	local name = savedir.. minetest.pos_to_string(pos).."_"..num..".schematic"
   local f=io.open(name,"r")
   if f~=nil then io.close(f) return true else return false end
end

gates.cache = {}

function gates.create_schematic(p1, p2, filename)
	minetest.create_schematic(p1, p2,{},savedir.. filename)
	--gates.cache[filename] = minetest.serialize_schematic(savedir..filename, "lua", {})
	--minetest.log("error", gates.cache[filename])
end

function gates.place_schematic(p, filename)
	local schematic = minetest.serialize_schematic(savedir..filename, "lua", {}) or ""
	-- load filename and serialize to string. append return{} so that the table can read by deserialize
	local schematic_STR = "return { "..schematic.."}"
	-- get the schematic by deserializing the shematic-string
	local schematic = minetest.deserialize(schematic_STR)
	-- finaly place the shematic
	return minetest.place_schematic(p, schematic.schematic, nil, nil, true)
end

local function sort_pos(pos1, pos2)
	if pos1.x > pos2.x then
		pos2.x, pos1.x = pos1.x, pos2.x
	end
	if pos1.y > pos2.y then
		pos2.y, pos1.y = pos1.y, pos2.y
	end
	if pos1.z > pos2.z then
		pos2.z, pos1.z = pos1.z, pos2.z
	end
	return pos1, pos2
end

local function volume(pos1, pos2)
	return (pos2.x - pos1.x + 1) *
		   (pos2.y - pos1.y + 1) *
		   (pos2.z - pos1.z + 1)
end

local function get_form(pos)
	local meta = minetest.get_meta(pos);
	local page = meta:get_int("page");

	local form = ""

	if page == 1 then
		form = form_page1:format(meta:get_string("pos1"),meta:get_string("pos2"));
		if worldedit then
			form = form .. 	"button[0.1,4.5;7.8,0;read_worldedit;Read pos1 and pos2 from worldedit]" .. "tooltip[read_worldedit;Search in inventory for worldedit wand tool]"
		end

	elseif page == 2 then --display page 2
		local state = meta:get_string("state")

		if state  == "closed" then
			form = form_page2_closed
		elseif state == "opend" then
			form = form_page2_opend
		else
			form = form_page2
		end
	elseif page >= 3 then --page 3: Finished
		local state = meta:get_string("state");
		form = form_page3:format(state)
	else	--page 0 Init
		form = form_page0
	end
	return form
end

local function placed(pos, placer, itemstack, pointed_thing)
	local meta = minetest.get_meta(pos);
	local form = get_form(pos);
	meta:set_string("formspec",form);
	meta:set_string("owner",placer:get_player_name());
	meta:set_string("infotext","Right click to init");
end

local function close_gate(pos)
	--local start_time = os.clock()
	local meta = minetest.get_meta(pos);
	if meta:get_string("state") == "opend" then -- only close the gate if its closed
		local pos1 = minetest.string_to_pos(meta:get_string("pos1"))
		local pos2 = minetest.string_to_pos(meta:get_string("pos2"))
		gates.create_schematic(pos1, pos2, minetest.pos_to_string(pos).."_opend.schematic")--save the opend gate before closing
		if gates.place_schematic(pos1, minetest.pos_to_string(pos).."_closed.schematic") then
			meta:set_string("state","closed");
			meta:set_string("infotext","closed");
			minetest.swap_node(pos, {name="gates:controler_closed"})
		else
			minetest.log("error","GATES: Could not place shematic")
		end
	end
	--local end_time = os.clock()
	--elapsed_time = end_time-start_time
	--print('start time: '   .. start_time .. 's')
	--print('end time: '     .. end_time .. 's')
--print('time elapsed: ' .. elapsed_time .. 's')
end

local function open_gate(pos)
	--local start_time = os.clock()
	local meta = minetest.get_meta(pos);
	if meta:get_string("state") == "closed" then -- only open the gate if its closed
		local pos1 = minetest.string_to_pos(meta:get_string("pos1"))
		local pos2 = minetest.string_to_pos(meta:get_string("pos2"))
		gates.create_schematic(pos1, pos2, minetest.pos_to_string(pos).."_closed.schematic")--save the closed gate before opening
		if gates.place_schematic(pos1, minetest.pos_to_string(pos).."_opend.schematic") then
			meta:set_string("state","opend");
			meta:set_string("infotext","opend");
			minetest.swap_node(pos, {name="gates:controler_opend"})
		else
			minetest.log("error","GATES: Could not place shematic")
		end
	end
	--local end_time = os.clock()
	--elapsed_time = end_time-start_time
	--print('start time: '   .. start_time .. 's')
	--print('end time: '     .. end_time .. 's')
	--print('time elapsed: ' .. elapsed_time .. 's')
end

local function toggle(pos)
	local meta = minetest.get_meta(pos);
	if meta:get_string("state") == "opend" then
		close_gate(pos);
		return "closed"
	else
		open_gate(pos);
		return "opend"
	end
end

local function destruct(pos)
	os.remove(savedir..minetest.pos_to_string(pos).."_opend.schematic")
	os.remove(savedir..minetest.pos_to_string(pos).."_closed.schematic")
end

local msg_too_far = "%s is too far away from gate controler. Max %d is alowed, you have %g distance!"
local  function submitted(pos, formname, fields, sender)
if fields.quit then return end;
	meta = minetest.get_meta(pos);
	page = meta:get_int("page") or 0;

	if fields.next and page == 0 then
		meta:set_int("page",1);
	elseif fields.next and fields.pos1 and fields.pos2 then --check if correct node is entered
		local pos1 = minetest.string_to_pos(fields.pos1);
		if pos1 == nil then
			minetest.chat_send_player(sender:get_player_name(), "Not a valid Position 1 entered. Have to be in format (x,y,z)")
			return
		end
		local pos2 = minetest.string_to_pos(fields.pos2);
		if pos2 == nil then
			minetest.chat_send_player(sender:get_player_name(), "Not a valid Position 2 entered. Have to be in format (x,y,z)")
			return
		end
		--print(vector.distance(pos, pos1));
		--print(gates.max_distance);
		if vector.distance(pos, pos1) > gates.max_distance then
			minetest.chat_send_player(sender:get_player_name(), msg_too_far:format("Pos1", gates.max_distance, vector.distance(pos, pos1) ))
			return
		end
		if vector.distance(pos, pos2) > gates.max_distance then
			minetest.chat_send_player(sender:get_player_name(), msg_too_far:format("Pos2", gates.max_distance, vector.distance(pos, pos2) ))
			return
		end
		local smallest,biggest = sort_pos(pos1,pos2)
		local volume = volume(smallest, biggest)
		--print("volume:",volume);
		if volume > gates.max_volume then
			minetest.chat_send_player(sender:get_player_name(),
										string.format("The volume is too big! Max %d is alowed. Your volume would be %d.", gates.max_volume, volume)
										)
		return
		end
		meta:set_string("pos1", minetest.pos_to_string(smallest))
		meta:set_string("pos2", minetest.pos_to_string(biggest))
		meta:set_int("page",2);

	end

	if fields.build_opend then
		meta:set_string("state","opend");
		meta:set_string("infotext","Now build a gate wich is »open«.");
	elseif fields.build_closed then
		meta:set_string("state","closed");
		meta:set_string("infotext","Now build a gate wich is »closed«.");
	end

	if fields.opend_finisched then
		local pos1 = minetest.string_to_pos(meta:get_string("pos1"))
		local pos2 = minetest.string_to_pos(meta:get_string("pos2"))
		minetest.create_schematic(pos1, pos2,{}, savedir..minetest.pos_to_string(pos).."_opend.schematic")
		if schematic_exists(pos,"closed") then --if there is a closed one build
			meta:set_int("page",3);--set page to page 3
			meta:set_string("infotext","opend");
			minetest.swap_node(pos, {name="gates:controler_opend"})
		else
			meta:set_string("state","closed");
			meta:set_string("infotext","Now build a gate wich is »closed«.");
		end
	elseif fields.closed_finisched then
		local pos1 = minetest.string_to_pos(meta:get_string("pos1"))
		local pos2 = minetest.string_to_pos(meta:get_string("pos2"))
		minetest.create_schematic(pos1, pos2,{}, savedir..minetest.pos_to_string(pos).."_closed.schematic")
		if schematic_exists(pos,"opend") then --if there is a opend one build
			meta:set_int("page",3);--set page to page 3
			meta:set_string("infotext","closed");
			minetest.swap_node(pos, {name="gates:controler_closed"})
		else
			meta:set_string("state","opend");
			meta:set_string("infotext","Now build a gate wich is »opend«.");
		end
	end
	if fields.read_worldedit then
	local name = sender:get_player_name()
		minetest.chat_send_player(name, "read worldedit");
		local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name];
		if pos1 and pos2 then
			meta:set_string("pos1",minetest.pos_to_string(pos1));
			meta:set_string("pos2",minetest.pos_to_string(pos2));
		else
			minetest.chat_send_player(name, "No worldedit region selected");
		end
	end
	if fields.cb_read1 then
	local name = sender:get_player_name()
		--minetest.chat_send_player(name, "read from clipboard");
		local pos1 = gates.clipboard[name];
		if pos1  then
			meta:set_string("pos1",minetest.pos_to_string(pos1));
		else
			minetest.chat_send_player(name, "Please punch (leftclick) a block");
		end
	end
	if fields.cb_read2 then
	local name = sender:get_player_name()
		--minetest.chat_send_player(name, "read from clipboard");
		local pos2 = gates.clipboard[name];
		if pos2  then
			meta:set_string("pos2",minetest.pos_to_string(pos2));
		else
			minetest.chat_send_player(name, "Please punch (leftclick) a block");
		end
	end
	if fields.toggle then
		local status = toggle(pos);
		if status == "opend" then
		minetest.chat_send_player(sender:get_player_name(), "Gate Opened")
		elseif status == "closed" then
		minetest.chat_send_player(sender:get_player_name(), "Gate Closed")
		else
		minetest.chat_send_player(sender:get_player_name(), "ERROR: Unknown state of Gate")
		end
	end
	if fields.setup then
		meta:set_int("page",0);
		meta:set_string("state","?");
		minetest.swap_node(pos, {name="gates:controler"})
		destruct(pos)
	end
	if fields.prev then
		meta:set_int("page",page-1);
	end
	meta:set_string("formspec",get_form(pos))
end

local function punched(pos, node, puncher, pointed_thing)
	--print(schematic_exists(pos,"closed"),schematic_exists(pos,"opend"))
	local playername = puncher:get_player_name()
	local status = toggle(pos);
	if status == "opend" then
		minetest.chat_send_player(playername, "Gate Opened")
	elseif status == "closed" then
		minetest.chat_send_player(playername, "Gate Closed")
	else
		minetest.chat_send_player(playername, "ERROR: Unknown state of Gate")
	end
end



minetest.register_node("gates:controler",{
	description ="Gate controller",
	tiles = {"gates_bg.png","gates_bg.png","gates_bg.png^gates_unknown.png"},
	groups = {oddly_breakable_by_hand = 1},
	after_place_node = placed,
	on_receive_fields = submitted,
	on_destruct = destruct,--function(pos, node, puncher, pointed_thing)
	mesecons = {
		effector = {
			rules = {
				{x = 1, y = 0, z = 0},
				{x =-1, y = 0, z = 0},
				{x = 0, y = 0, z = 1},
				{x = 0, y = 0, z =-1}
			},
			--action_on = open_gate,
			--action_off = close_gate--function (pos, node)
			--action_change = function (pos, node)
		}
	}
})

minetest.register_node("gates:controler_opend",{
	description ="Gate controller (you hacker you!)",
	tiles = {"gates_bg.png","gates_bg.png","gates_bg.png^gates_opend.png"},
	groups = {oddly_breakable_by_hand = 1, not_in_craft_guide = 1, not_in_creative_inventory = 1},
	after_place_node = placed,
	on_receive_fields =submitted,
	on_punch = punched,
	on_destruct = destruct,--function(pos, node, puncher, pointed_thing)
	drop = "gates:controler",
	mesecons = {
		effector = {
			rules = {
				{x = 1, y = 0, z = 0},
				{x =-1, y = 0, z = 0},
				{x = 0, y = 0, z = 1},
				{x = 0, y = 0, z =-1}
			},
			action_on = open_gate,
			action_off = close_gate--function (pos, node)
			--action_change = function (pos, node)
		}
	}
})

minetest.register_node("gates:controler_closed",{
	description ="Gate controller (you hacker you!)",
	tiles = {"gates_bg.png","gates_bg.png","gates_bg.png^gates_closed.png"},
	groups = {oddly_breakable_by_hand = 1, not_in_craft_guide = 1, not_in_creative_inventory = 1},
	after_place_node = placed,
	on_receive_fields =submitted,
	on_punch = punched,
	on_destruct = destruct,--function(pos, node, puncher, pointed_thing)
	drop = "gates:controler",
	mesecons = {
		effector = {
			rules = {
				{x = 1, y = 0, z = 0},
				{x =-1, y = 0, z = 0},
				{x = 0, y = 0, z = 1},
				{x = 0, y = 0, z =-1}
			},
			action_on = open_gate,
			action_off = close_gate--function (pos, node)
			--action_change = function (pos, node)
		}
	}
})

--clipboard action:
gates.clipboard = {};

minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing)
	if node.name ~= "air" then
		local name = puncher:get_player_name();
		if gates.clipboard then
		gates.clipboard[name] = pos;
		else
		gates.clipboard={};
		gates.clipboard[name] = pos;
		end
		--print("saved pos to clipboard");
	end
end)
