%% dot_write( +File, +Graph ).
%
% Graph should be as those produced by read_dot/2.
%
%
% @author nicos angelopoulos
% @version  0.1 2014/5/29
%
dot_write( File, Graph ) :-
	phrase( dot_spit(Graph), String ),
	setup_call_cleanup(
	    open(File, write, Out ),
	    format(Out, '~s', [String]),
	    close(Out) ).

dot_spit( dot(Strict,Gtype,Id,Stmts) ) -->
	dot_spit_graph_strict( Strict ), 
	dot_spit_atom( Gtype ), " ",
	dot_spit_atom( Id ),
	" {", "\n",
	dot_spit_statements( Stmts ),
	"}".

dot_spit_statements( [] ) --> "".
dot_spit_statements( [H|T] ) -->
	"\t",
	{ debug(dot_write, 'Statement:~w', H )},
	dot_spit_statement( H ),
	";\n",
	dot_spit_statements( T ).

dot_spit_statement( attr(Atype,Attrs) ) -->
	dot_spit_atom( Atype ), 
	" ",
	dot_spit_attrs( Attrs ).

dot_spit_statement( Var=Val ) -->
	dot_spit_id( Var ),
	"=",
	dot_spit_id( Val ).

dot_spit_statement( node(Id,Attrs) ) -->
	dot_spit_id( Id ),
	dot_spit_attrs( Attrs ).
dot_spit_statement( edge(IdOpChain,Attrs) ) -->
	dot_spit_id_op_chain( IdOpChain ),
	dot_spit_attrs( Attrs ).
dot_spit_statement( subgraph(Id,Stmts) ) -->
	"subgraph ",
	dot_spit_id( Id ),
	"{",
	dot_spit_statements( Stmts ),
	"}".
dot_spit_statement( nl ) --> "\n".

dot_spit_id_op_chain( [Id|T] ) --> 
	dot_spit_id( Id ),
	dot_spit_id_op_chain_rhs( T ).

dot_spit_id_op_chain_rhs( [] ) --> "".
dot_spit_id_op_chain_rhs( [Op,Id|T] ) --> 
	dot_spit_edge_op( Op ),
	dot_spit_id( Id ),
	dot_spit_id_op_chain_rhs( T ).

dot_spit_id( quote(Id) ) --> 
	 { !, atom_codes(Id,IdCs) },
	 "\"",IdCs, "\"".
dot_spit_id( html(Id) ) --> 
	 { !, atom_codes(Id,IdCs) },
	 "<",IdCs, ">".
dot_spit_id( Id ) --> 
	 {atom(Id)},
	 !,
	 dot_spit_id_atom( Id ).
dot_spit_id( Id ) --> 
	{number(Id),
	 number_codes(Id,IdCs) },
	IdCs.

% fixme: more specials? 
dot_spit_id_atom( Id ) -->
	{Specials = ['-'], member(Spc,Specials), sub_atom(Id,_,_,_,Spc),
	 atom_codes(Id,IdCs) },
	!,
	[0'"],
	IdCs,
	[0'"].
dot_spit_id_atom( Id ) -->
	{atom_codes(Id,IdCs) },
	IdCs.

dot_spit_attrs( InAttrs ) -->
	{to_nest_list( InAttrs, Attrs )},
	dot_spit_nest_attrs( Attrs ).

dot_spit_nest_attrs( [] ) --> "".
dot_spit_nest_attrs( [Al|Als] ) -->
	"[",
	dot_spit_attrs_list( Al ),
	"]",
	dot_spit_nest_attrs( Als ).

dot_spit_attrs_list( [] ) --> "".
dot_spit_attrs_list( [A|As] ) -->
	dot_spit_attr( A ),
	dot_spit_attrs_sep( As, Bs ),
	dot_spit_attrs_list( Bs ).

dot_spit_attr( Var=Val ) --> 
    !,
	dot_spit_id( Var, Val ).
dot_spit_attr( Comp ) -->      % be relaxed
	{ compound( Comp ),
	   ground( Comp ),
	   Comp =.. [Var,Val] },
	dot_spit_id( Var, Val ).

dot_spit_id( Var, Val ) -->
	dot_spit_id( Var ),
	"=",
	dot_spit_id( Val ).

dot_spit_attrs_sep( [Sep|Bs], Bs ) -->
	dot_spit_attrs_sep_token( Sep ),
	" ",
    !.
% dot_spit_attrs_sep( Bs, Bs ) -->    "".     % be relaxed about missing seps
dot_spit_attrs_sep( [], [] ) -->  !, "".
dot_spit_attrs_sep( Bs, Bs ) -->  ",".

dot_spit_attrs_sep_token( ',' ) --> ",".
dot_spit_attrs_sep_token( ';' ) --> ";".

dot_spit_graph_strict( false ) --> [].
dot_spit_graph_strict( true ) --> ["strict "].

dot_spit_edge_op( -> ) --> " -> ".
dot_spit_edge_op( -- ) --> " -- ".

dot_spit_atom( Gtype ) --> 
	{atom_codes(Gtype,GtypeCs)},
	GtypeCs.

to_nest_list( [H|T], Nest ) :-
	is_list(H),
	!,
	Nest = [H|T].
to_nest_list( Flat, [Flat] ).
