package Wiki::Toolkit::Node;
use base Wiki::Toolkit::Base;
our $VERSION = '0.00_01';
use strict; use warnings; use Carp;

=head1 METHODS

=over 4

=item B<node manipulation>

  new, is_latest, is_exactly_latest

=item B<node storage>

  source, exists, save

=item B<node data>

  content, metahash, metadata, meta2text, text2meta, grep_metadata

=item B<metadata accessors>

  name, ident, title + as specified in new

=back

=cut


my $META_SPLIT = qr/\s*,\s+/;
my $META_JOIN  = ', ';

sub new {
  my $class = shift;
  return $class->SUPER::new( source => undef,
                             content => undef,
                             metadata => undef,
                             metadata_accessors => [qw( version )],
                             @_ );
  
}

sub name {
  my $self = shift;
  return $self->metadata('name',@_);
}

sub ident {
  my $self = shift;
  return ($self->name,$self->version);
}

sub title {
  my $self = shift;
  return $self->metadata('title') 
    ? $self->metadata('title',@_)
    : $self->metadata('name',@_);
}
    
sub is_latest {
  my $self = shift;
  return 0 unless $self->source;
  return ($self->source->latest_version($self->name) <= $self->version);
}

sub is_exactly_latest {
  my $self = shift;
  return 0 unless $self->source;
  return ($self->source->latest_version($self->name) == $self->version);
}

sub source {
  my $self = shift;
  $self->{source} = shift if @_;
  return $self->{source};
}

sub exists {
  my $self = shift;
  return 0 unless $self->source;
  return $self->source->node_exists($self->ident);
}

sub save {
  my $self = shift;
  return undef unless $self->source;
  return $self->source->save_node($self);
}

sub content {
  my $self = shift;
  $self->{content} = shift if @_;
  $self->{content} = $self->source->read_content($self->ident) 
    if (!defined $self->{content} && $self->exists);
  return $self->{content};
}

sub metahash {
  my $self = shift;
  return wantarray ? %{$self->{metadata}} : \%{$self->{metadata}};
  # FIXME: I want to return a ref to a /copy/ of meta, did I do it correctly?
}

sub metadata {
  my $self = shift;

  # handle whole hashes
  return $self->metahash unless @_;
  if (@_ == 1 && ref $_[0] eq 'HASH') {
    %{$self->{metadata}} = %{$_[0]};
    return $self->metahash;
  }

  # otherwise handle individual elements
  my $key = shift;
  $self->{metadata}{$key} = join $META_JOIN, @_ if @_;
  return wantarray 
    ? split /$META_SPLIT/, $self->{metadata}{$key}
    : $self->{metadata}{$key};
}

sub grep_metadata {
  my ($self,$key,$search) = @_;
  my @val = $self->metadata($key);
  if (!ref $search) {
    return grep /^$search$/, @val;
  } elsif (ref $search eq 'CODE') {
    return &$search( @val );
  } else {
    return undef;
  }
}

        
# the following two functions are lifted and modified from Text::Header v1.03

sub meta2text {
  my $invocant = shift;
  my (@text, %meta);

  if (@_) { 
    %meta = @_;
  } elsif ( ref $invocant ) {
    %meta = $invocant->metahash();
  }
  
  foreach my $key (keys %meta) {
    $key = ucfirst lc $key;
    $key =~ s/[-_](\w)/-\u$1/g;
    my $val = $meta{$key};
    $val = join $META_JOIN, @$val if ref $val eq 'ARRAY';
    push @text, "$key: $val\n";
  }
  return join "", @text;
}
      
sub text2meta {
  my $invocant = shift;
  my @lines = split /\n/, shift;
  chomp @lines;
  my (%meta, $i);

  $i = 0;
  while (my $line = $lines[$i]) {

    # join multiple indented lines per RFC 822
    $line .= $lines[$i] while (defined $lines[++$i] && $lines[$i] =~ /^\s+/);

    # split the two and change the tag to lowercase
    my ($tag, $val) = $line =~ m/([-\w]+)\s*:\s*(.*)/s;
    $tag = lc $tag;
    $tag =~ s/-/_/g;

    # some cleanup                                                                 
    $val =~ s/\n\s*/ /g;
    $val =~ s/$META_SPLIT/$META_JOIN/g;

    # either stuff it into a hash or, if already exists, append with comma separator
    $meta{$tag} = (defined $meta{$tag})
      ? $meta{$tag} . $META_JOIN . $val
      : $val;

  }

  if ( ref $invocant ) {
    $invocant->metadata(\%meta);
    return $invocant;
  } else {
    return wantarray ? %meta : \%meta;
  }
}


sub AUTOLOAD {
  my $self = shift;
  my $class = ref $self;
  my $method = our $AUTOLOAD;
  return if $method =~ /::DESTROY$/;

  croak "Can't AUTOLOAD $method for you as class (try using an instance)"
    unless $class;

  $method =~ s/$class\:\://;
  croak "$method is not a valid method"
    unless grep /^$method$/, @{$self->{metadata_accessors}};

  return $self->metadata($method,@_);
}
