#############################################################################
# output the graph in dot-format text
#
# (c) by Tels 2004-2005.
#############################################################################

package Graph::Easy::As_graphviz;

$VERSION = '0.03';

#############################################################################
#############################################################################

package Graph::Easy;

use strict;

sub _as_graphviz
  {
  my ($self) = @_;

  # convert the graph to a textual representation
  # does not need a layout() beforehand!

  # generate the class attributes first
  my $txt = 'digraph NAME { ' .
            "\n# Generated by Graph::Easy $Graph::Easy::VERSION" .
	    " at " . scalar localtime() . "\n";

  # per default, our nodes are rectangular
  $txt .= "  node [shape=record];\n\n";

  # XXX TODO: attributes and groups are likely not correct yet

  my $att =  $self->{att};
  for my $class (sort keys %$att)
    {
    my $a = $att->{$class};
    my $att = '';
    for my $atr (keys %$a)
      {
      # attribute not defined
      next if !defined $a->{$atr};

      next if defined $self->{def_att}->{$class}->{$atr} &&
              $a->{$atr} eq $self->{def_att}->{$class}->{$atr};
      $att .= "  $atr: $a->{$atr};\n";
      }

    if ($att ne '')
      {
      # the following makes short, single definitions to fit on one line
      if ($att !~ /\n.*\n/ && length($att) < 40)
        {
        $att =~ s/\n/ /; $att =~ s/^  / /;
        }
      else
        {
        $att = "\n$att";
        }
      $txt .= "$class {$att}\n";
      }
    }

  $txt .= "\n" if $txt ne '';		# insert newline

  my @nodes = $self->sorted_nodes();

  my $count = 0;
  # output nodes with attributes first, sorted by their name
  foreach my $n (sort { $a->{name} cmp $b->{name} } @nodes)
    {
    $n->{_p} = undef;			# mark as not yet processed
    my $att = $n->attributes_as_graphviz();
    if ($att ne '')
      {
      $n->{_p} = 1;			# mark as processed
      $count++;
      $txt .= $n->as_graphviz_txt() . $att . "\n"; 
      }
    }
 
  $txt .= "\n" if $count > 0;		# insert a newline

  # output groups first, with their nodes
  foreach my $gn (sort keys %{$self->{groups}})
    {
    my $group = $self->{groups}->{$gn};
    $txt .= $group->as_txt();		# marks nodes as processed if nec.
    $count++;
    }

  foreach my $n (@nodes)
    {
    my @out = $n->successors();
    my $first = $n->as_graphviz_txt();
    if ((@out == 0) && ( (scalar $n->predecessors() || 0) == 0))
      {
      # single node without any connections (unless already output)
      $txt .= $first . "\n" unless defined $n->{_p};
      }
    # for all outgoing connections
    foreach my $other (reverse @out)
      {
      my $edge = $self->edge( $n, $other );
      $txt .= $first . " -> " . $other->as_graphviz_txt() . "\n";
      }
    }

  $txt .  "\n}\n";	# close the graph again
  }

package Graph::Easy::Node;

sub attributes_as_graphviz
  {
  # return the attributes of this node as text description
  my $self = shift;

  my $att = '';
  my $class = $self->class();
  my $a = $self->{att};
  for my $atr (sort keys %$a)
    {
    # attribute not defined
    next if !defined $a->{$atr};

    # attribute defined, but same as default
    if (defined $self->{graph})
      {
      my $DEF = $self->{graph}->attribute ($class, $atr);
      next if defined $DEF && $a->{$atr} eq $DEF;
      }

    my $val = $a->{$atr};
    # encode critical characters
    $val =~ s/([;\x00-\x1f])/sprintf("%%%02x",ord($1))/eg;

    $att .= "$atr=$val, ";
    }
  # include our subclass as attribute
  $att .= "class: $1; " if $class =~ /\.(\w+)/;

  $att =~ s/,\s$//;             # remove last ","

  # generate attribute text if nec.
  $att = ' [ ' . $att . ' ]' if $att ne '';

  $att;
  }

sub as_graphviz_txt
  {
  my $self = shift;

  my $name = $self->{att}->{label}; $name = $self->{name} unless defined $name;

  # quote special chars in name
  $name =~ s/([\[\]\(\)\{\}\#])/\\$1/g;

  '"' .  $name . '"';
  }
 
1;
__END__
=head1 NAME

Graph::Easy::As_graphviz - Generate graphviz description from graph object

=head1 SYNOPSIS

	use Graph::Easy;
	
	my $graph = Graph::Easy->new();

	my $bonn = Graph::Easy::Node->new(
		name => 'Bonn',
	);
	my $berlin = Graph::Easy::Node->new(
		name => 'Berlin',
	);

	$graph->add_edge ($bonn, $berlin);

	print $graph->as_graphviz();

	# prints something like:

	# digraph NAME { Bonn -> Berlin }

=head1 DESCRIPTION

C<Graph::Easy::As_graphviz> contains just the code for converting a
L<Graph::Easy|Graph::Easy> object to a textual description suitable for
feeding it to graphviz.

=head1 EXPORT

Exports nothing.

=head1 SEE ALSO

L<Graph::Easy>.

=head1 AUTHOR

Copyright (C) 2004 - 2005 by Tels L<http://bloodgate.com>

See the LICENSE file for information.

=cut
