local	NODE_BASIC	= 0;
local	NODE_SPRITE = 1;
local	NODE_ANIM 	= 2;
local	NODE_PS 	= 3;
local	NODE_TEXT	= 4;
local	NODE_DIST	= 5;
local	NODE_FLASH	= 6;
local	NODE_LIGHTNING	= 7;

function CopyNode( old, new )
	NODES[new] = clone( NODES[old] );
	NODES[new].name = new;
end

function SaveRaw( data )
	local result = "";
	if data ~= nil then
		local i, v = next(data, nil)  -- i is an index of t, v = t[i]
		while i do
			if type(v)=="table" then
				result = result .. i .. " = {" .. SaveRaw( v ) .. "}, ";
			else
				result = result .. i .. " = \"" .. tostring( v ) .. "\", ";
			end;
			i, v = next(data, i);       -- get next index
		end
	else
		result = "nil";
	end
	return result;
end

function	NodeData( data )
	data.x = data.x or 0;
	data.y = data.y or 0;
	data.angle = data.angle or 0;
	data.hscale = data.hscale or 1;
	data.vscale = data.vscale or data.hscale;
	data.color = data.color or { 255, 255, 255, 255 };
	data.z = data.z or 0;
	data.layer = data.layer or 0;
	
	--	convert angle from degrees to radians
	data.angle = data.angle * 0.017453292519943295769236907684886;
	
	return data;
end


function NodeRegister( data )

	if NODES == nil then
		NODES = {};
	end
	
	data.file = CURRENT_FILE;
	data.node_data = data.node_data or NodeData{};
	
	if data.fx ~= nil then
		if data.die_with_fx_stop == nil then
			data.die_with_fx_stop = 0;
		end
	end
	
	if type(data.name) ~= "string" then
		error("node name not specified");
	else
		if ( data.is_child ) then
			return data;
		else
			NODES[data.name] = data;
		end
	end
end


function	BasicNode( data )

	data["type"] = NODE_BASIC;
	
	return NodeRegister( data );

end

function	SpriteNode( data )

	
	data["type"] = NODE_SPRITE;

	
	if type(data.image) ~= "string" then
		error("node image not specified");
	elseif IMAGES[data.image] == nil then
		error("image [" .. data.image .. "] not exist");
	else
		return NodeRegister( data );
	end
end

function	AnimNode( data )
	
	data["type"] = NODE_ANIM;
	
	if type(data.image) ~= "string" then
		error("node image not specified");
	elseif IMAGES[data.image] == nil then
		error("image [" .. data.image .. "] not exist");
	elseif type(data.counter) ~= "string" then
		error("node counter not specified");		
	else
		return NodeRegister( data );
	end
end



function	PSNode( data )
	
	data["type"] = NODE_PS;
	
	if data.move_particles == nil then
		data.move_particles = 0;
	end	
	if data.die_with_ps_stop == nil then
		data.die_with_ps_stop = 1;
	end
	if data.emiter_affected_ndata == nil then
		data.emiter_affected_ndata = 1;
	end
	if data.live_until_all_particles_die == nil then
		data.live_until_all_particles_die = 1;
	end
--[[	
	if data.fire_on_first_render == nil then
		data.fire_on_first_render = 1;
	end
]]
	if data.pause_before_fire == nil then
		data.pause_before_fire = 0;
	end
	
	--[[
	if type(data.image) ~= "string" then
		error("node image not specified");
	elseif IMAGES[data.image] == nil then
		error("image [" .. data.image .. "] not exist");
	elseif type(data.ps_file) ~= "string" then
		error("node ps_file not specified");
	else
		return NodeRegister( data );
	end
	]]
	return NodeRegister( data );
end

function	TextNode( data )
	
	data["type"] = NODE_TEXT;
	
	data.text = data.text or "";
	
	if data.max_width == nil then
		data.max_width = 0;
	end	
	
	if data.vivid_letters == nil then
		data.vivid_letters = 1;
	end
	
	if type(data["v_al"]) == "number" then
		if data["v_al"] < 0 or data["v_al"] > 2 then
			data["v_al"] = 0;
		end
	else
		data["v_al"] = 0;
	end
	
	if type(data["h_al"]) == "number" then
		if data["h_al"] < 0 or data["h_al"] > 2 then
			data["h_al"] = 0;
		end
	else
		data["h_al"] = 0;
	end
	
	
	if type(data.font) ~= "string" then
		error("node font not specified");
	else
		return NodeRegister( data );
	end
end

function	DistortionNode( data )
	
	data["type"] = NODE_DIST;
	

	data.rows = data.rows or 2;
	data.columns = data.columns or 2;
	
	
	if type(data.image) ~= "string" then
		error("node image not specified");
	elseif IMAGES[data.image] == nil then
		error("image [" .. data.image .. "] not exist");
	else
		return NodeRegister( data );
	end
end

function	FlashNode( data )
	
	data["type"] = NODE_FLASH;
	

	data.rows = data.width or 10;
	data.columns = data.heigth or 10;
	
		
	return NodeRegister( data );
	
end

function	LightningNode( data )
	
	data["type"] = NODE_LIGHTNING;
	

	data.stay_time = data.stay_time or 5;
	
	data.start_x = data.start_x or 50;
	data.start_y = data.start_y or 50;
	data.end_x = data.end_x or 500;
	data.end_y = data.end_y or 500;
	
	--	длинна сегметна
	data.segment_length = data.segment_length  or 20;
	--	ширина семента
	data.segment_width = data.segment_width  or 5;
	--	масимамльное отклонение сегмента от направления на конец
	data.delta_angle = data.delta_angle  or 30;
	
	--  макс отклонение
	data.shape_amplitude = data.shape_amplitude  or 40;
	-- частота, влияет на количество изгибов ~3 - один период(дуга), 6-два периода (однин изгиб), ...
	data.shape_freq = data.shape_freq  or 4;
	-- начальная фаза
	data.shape_phase = data.shape_phase  or 0;
	
	-- параметры проявления молнии ai = alfa1*(time-alfa2*i), ai - значение альфы i-того узла
	data.alfa1 = data.alfa1  or 10;
	data.alfa2 = data.alfa2  or 0.005;
	
	--параметры колебания молнии amplitude*cos( freqency*( time-velocity*i ) )
	data.amplitude	= data.amplitude  or 0;
	data.frequency	= data.frequency  or 9;
	data.velocity	= data.velocity  or 0.09;
	
	data.amplitude2	= data.amplitude2  or 0;
	data.frequency2	= data.frequency2  or 30;
	data.velocity2	= data.velocity2  or 0.045;
	
		
	return NodeRegister( data );
	
end


--	SAVING FUCTION. FOR NODE EDITOR

function SaveNodeData( data )
	local result = "NodeData{";

	if data.x ~= 0 then
		result = result .. "x = " .. data.x .. ",";
	end
	if data.y ~= 0 then
		result = result .. "y = " .. data.y .. ",";
	end
	if data.angle ~= 0 then
		result = result .. "angle = " .. (data.angle/0.017453292519943295769236907684886) .. ",";
	end
	if data.hscale ~= 1 or data.vscale ~= 1 then
		result = result .. "hscale = " .. data.hscale .. ",";
		result = result .. "vscale = " .. data.vscale .. ",";
	end	
	if data.z ~= 0 then
		result = result .. "z = " .. data.z .. ",";
	end
	if data.layer ~= 0 then
		result = result .. "layer = " .. data.layer .. ",";
	end
	if data.color[1] ~= 255 or data.color[2] ~= 255 or data.color[3] ~= 255 or data.color[4] ~= 255 then
		result = result .. "color = {" .. data.color[1] .. ', ' .. data.color[2] .. ', ' .. data.color[3] .. ', ' .. data.color[4] .. "},";
	end
	
	result = result .. "};"
	return result;
end

function SaveChildren( data )
	local result = "";
	
	if data.children ~= nil then
		result = "children = { ";
		for i,v in ipairs(data.children) do
			result = result .. "\"" .. v .."\", ";
		end
		result = result .. "};";
	end
	
	return result;
end

function SaveFx( data )
	local result = "";
	
	if data.fx ~= nil then
		result = result .. "fx = \"" .. data.fx .. "\", ";
		if ( data.fx_end_coords ~= nil ) then
			result = result .. "fx_end_coords = {" .. data.fx_end_coords[1] .. ", " .. data.fx_end_coords[2] .. "}, ";
		end
		result = result .. "die_with_fx_stop = " .. tostring(data.die_with_fx_stop) .. ", ";
	end
	
	return result;
end

function SaveBasicNode( data )
	local result = "BasicNode{";
	
	result = result .. "name = \"" .. data.name .. "\",";
	if  type(data.id) == "string" and data.id ~= "" then
		result = result .. "id = \"" .. data.id .. "\",";
	end
	result = result .. "node_data = " .. SaveNodeData( data.node_data );
	result = result .. SaveFx(data);
	result = result .. SaveChildren(data);

	result = result .. "};"
	return result;
end

function SaveSpriteNode( data )
	local result = "SpriteNode{";
	
	result = result .. "name = \"" .. data.name .. "\",";
	if  type(data.id) == "string" and data.id ~= "" then
		result = result .. "id = \"" .. data.id .. "\",";
	end
	result = result .. "image = \"" .. data.image .. "\",";
	result = result .. "node_data = " .. SaveNodeData( data.node_data );
	result = result .. SaveFx(data);
	result = result .. SaveChildren(data);

	result = result .. "};"
	return result;
end

function SaveAnimNode( data )
	local result = "AnimNode{";
	
	result = result .. "name = \"" .. data.name .. "\",";
	if  type(data.id) == "string" and data.id ~= "" then
		result = result .. "id = \"" .. data.id .. "\",";
	end
	result = result .. "image = \"" .. data.image .. "\",";
	result = result .. "counter = \"" .. data.counter .. "\",";
	result = result .. "die_with_anim_stop = " .. tostring(data.die_with_anim_stop) .. ",";
	result = result .. "alfa_window = " .. tostring(data.alfa_window) .. ",";
	result = result .. "node_data = " .. SaveNodeData( data.node_data );
	result = result .. SaveFx(data);
	result = result .. SaveChildren(data);

	result = result .. "};"
	return result;
end


function SavePSNode( data )
	local result = "PSNode{";
	
	
	result = result .. "name = \"" .. data.name .. "\",";
	if  type(data.id) == "string" and data.id ~= "" then
		result = result .. "id = \"" .. data.id .. "\",";
	end
	result = result .. "image = \"" .. data.image .. "\",";
	result = result .. "ps_file = \"" .. string.gsub(data.ps_file, "\\", "\\\\" ) .. "\",";
	result = result .. "move_particles = " .. tostring(data.move_particles) .. ",";
	result = result .. "die_with_ps_stop = " .. tostring(data.die_with_ps_stop) .. ",";
	result = result .. "live_until_all_particles_die = " .. tostring(data.live_until_all_particles_die) .. ",";
	result = result .. "emiter_affected_ndata = " .. tostring(data.emiter_affected_ndata) .. ",";
	--result = result .. "fire_on_first_render = " .. tostring(data.fire_on_first_render) .. ",";
	result = result .. "pause_before_fire = " .. data.pause_before_fire .. ",";
	result = result .. "node_data = " .. SaveNodeData( data.node_data );
	result = result .. SaveFx(data);
	result = result .. SaveChildren(data);

	result = result .. "};"
	return result;
end


function PrepareTextToSave( text )
	local result = "";
	for i = 1,#text,1 do
		local i = string.byte( text, i );
		if i == string.byte( "\n", 1 ) then
			result = result .. "\\n";
		elseif i == string.byte( "\034", 1 ) then
			result = result .. "\\034";
		elseif i == string.byte( "\039", 1 ) then
			result = result .. "\\039";
		else
			result = result .. string.char(i);
		end		
	end
	--local result = string.gsub(text, "\n", "\\n");
	--result = string.gsub(text, "\034", "\\034");
	--result = string.gsub(text, "\039", "\\039");
	return result;
end

function SaveTextNode( data )
	local result = "TextNode{";
	
	result = result .. "name = \"" .. data.name .. "\",";
	if  type(data.id) == "string" and data.id ~= "" then
		result = result .. "id = \"" .. data.id .. "\",";
	end
	result = result .. "font = \"" .. data.font .. "\",";
	result = result .. "text = \"" .. PrepareTextToSave(data.text) .. "\",";
	result = result .. "v_al = " .. data.v_al .. ",";
	result = result .. "h_al = " .. data.h_al .. ",";
	result = result .. "max_width = " .. data.max_width .. ",";
	result = result .. "vivid_letters = " .. data.vivid_letters .. ",";
	if data.algorithm ~= nil then
		result = result .. "algorithm = {" .. SaveRaw( data.algorithm ) .. "},";
	end
	result = result .. "node_data = " .. SaveNodeData( data.node_data );
	result = result .. SaveFx(data);
	result = result .. SaveChildren(data);

	result = result .. "};"
	return result;
end

function SaveDistNode( data )
	local result = "DistortionNode{";
	
	result = result .. "name = \"" .. data.name .. "\",";
	if  type(data.id) == "string" and data.id ~= "" then
		result = result .. "id = \"" .. data.id .. "\",";
	end
	result = result .. "image = \"" .. data.image .. "\",";
	result = result .. "rows = " .. data.rows .. ",";
	result = result .. "columns = " .. data.columns .. ",";
	if data.algorithm ~= nil then
		result = result .. "algorithm = {" .. SaveRaw( data.algorithm ) .. "},";
	end
	result = result .. "node_data = " .. SaveNodeData( data.node_data );
	result = result .. SaveFx(data);
	result = result .. SaveChildren(data);

	result = result .. "};"
	return result;
end

function SaveLighningNode( data )
	local result = "LightningNode{";
	
	result = result .. "name = \"" .. data.name .. "\",";
	if  type(data.id) == "string" and data.id ~= "" then
		result = result .. "id = \"" .. data.id .. "\",";
	end
	
	if  type(data.image) == "string" and data.image ~= "" then
		result = result .. "image = \"" .. data.image .. "\",";
	end
	
	result = result .. "stay_time = " .. data.stay_time .. ",";
	
	result = result .. "start_x = " .. data.start_x .. ",";
	result = result .. "start_y = " .. data.start_y .. ",";
	result = result .. "end_x = " .. data.end_x .. ",";
	result = result .. "end_y = " .. data.end_y .. ",";

	result = result .. "segment_length = " .. data.segment_length .. ",";
	result = result .. "segment_width = " .. data.segment_width .. ",";
	
	result = result .. "shape_amplitude = " .. data.shape_amplitude .. ",";
	result = result .. "shape_freq = " .. data.shape_freq .. ",";
	result = result .. "shape_phase = " .. data.shape_phase .. ",";
	result = result .. "delta_angle = " .. data.delta_angle .. ",";
	
	result = result .. "amplitude = " .. data.amplitude .. ",";
	result = result .. "frequency = " .. data.frequency .. ",";
	result = result .. "velocity = " .. data.velocity .. ",";
	
	result = result .. "amplitude2 = " .. data.amplitude2 .. ",";
	result = result .. "frequency2 = " .. data.frequency2 .. ",";
	result = result .. "velocity2 = " .. data.velocity2 .. ",";
	
	result = result .. "alfa1 = " .. data.alfa1 .. ",";
	result = result .. "alfa2 = " .. data.alfa2 .. ",";
	
	result = result .. "node_data = " .. SaveNodeData( data.node_data );
	result = result .. SaveFx(data);
	result = result .. SaveChildren(data);

	result = result .. "};"
	return result;
end

NODE_SAVE = {
[NODE_BASIC]	= SaveBasicNode;
[NODE_SPRITE]	= SaveSpriteNode;
[NODE_ANIM]		= SaveAnimNode;
[NODE_PS]		= SavePSNode;
[NODE_TEXT]		= SaveTextNode;
[NODE_DIST]		= SaveDistNode;
[NODE_LIGHTNING]		= SaveLighningNode;
};

function SaveNodes( from_file, to_file )
	local myf = io.open( to_file, "w" );
	
	if myf then
		local sorted_nodes = {};
		local n = 1;
		
		for i,v in pairs(NODES) do
			sorted_nodes[n] = i;
			n = n + 1;
		end
		
		table.sort( sorted_nodes );
		
		for i = 1, #sorted_nodes do
			local v = NODES[sorted_nodes[i]];
			if v.file == from_file then
				myf:write( NODE_SAVE[v.type](v) );
				myf:write( "\n" );
			end
		end
		
--[[	
		for i,v in pairs(NODES) do
			if v.file == from_file then
				myf:write( NODE_SAVE[v.type](v) );
				myf:write( "\n" );
			end
		end
]]
	end
	
	myf:close();
end

function CheckRecursion( node, parents )
	if parents == nil then
		parents = {};
	end
	
	local result = false;
	
	if parents[node.name] == 1 then
		-- found recursion
		result = true;
	else
		if node.children ~= nil then
			parents[node.name] = 1;
			for i,v in pairs( node.children ) do
				result = result or CheckRecursion( NODES[v], parents );
			end
			parents[node.name] = nil;	
		end
	end
	
	return result;
	
end

function DataCheck()
	
	local result = false;
	
	if NODES ~= nil then
		for i,v in pairs( NODES ) do
			--local rc = CheckRecursion( v, nil );
			if rc == true then
				error( "recursion found in node [" .. i .. "]" );
			end
			result = result or rc;
		end
	end
	
	if result == true then
		error( "data not valid" );
	end
	
end


