use strict;
use warnings;

package minitree;

{ my( @parent, @next_sibling, @previous_sibling, @first_child, @name, @value, @attributes, @pos);
  my $last_obj=0;
  
  sub new
    { my $class= shift;
      my $att_class= shift;
      my %attributes= @_;
      
      $last_obj++;
      my $id= $last_obj;

      my $self= bless \$id, $class;
      
      $self->name(  $attributes{name});  delete $attributes{name};
      $self->value( $attributes{value}); delete $attributes{value};
      
      my @node_attributes= map { $att_class->new( $self, $_ => $attributes{$_}) } sort keys %attributes;
      $self->attributes( \@node_attributes);

      return $self;
    }


    
  BEGIN
    { foreach my $method ( qw( parent next_sibling previous_sibling first_child name value pos) )
        { no strict 'refs';
          *{$method}= sub { my $self= shift; 
                            if( @_) { ${$method}[$$self]= shift; } 
                            return ${$method}[$$self];
                          };
        }
    }

  sub attributes
    { my $self= shift;
      if( @_) { $attributes[$$self]= shift; } 
      return $attributes[$$self] || [];
    };
      

  sub root { my $self= shift; while( $self->parent) { $self= $self->parent; } return $self; }

  sub last_child
    { my $self= shift;
      my $child= $self->first_child || return;
      while( $child->next_sibling) { $child= $child->next_sibling; }
      return $child;
    }

  sub children
    { my $self= shift;
      my @children;
      my $child= $self->first_child || return;
      while( $child) { push @children, $child; $child= $child->next_sibling; }
      return @children;
    }


  sub add_as_last_child_of
    { my( $child, $parent)= @_;
      $child->parent( $parent);
      if( my $previous_sibling= $parent->last_child)
        { $previous_sibling->next_sibling( $child);
          $child->previous_sibling( $previous_sibling);
        }
      else
        { $parent->first_child( $child); }
      
    }

  sub set_pos
    { my $self= shift;
      my $pos = shift || 1;
      $self->pos( $pos++);
      foreach my $att (@{$self->attributes})
        { $att->pos( $pos++); }
      foreach my $child ($self->children)
        { $pos= $child->set_pos( $pos); }
      return $pos;
    }
          

  sub dump
    { my $self= shift;
      return   "$$self : " 
      # . join ( " - ", grep { $_ } map { "$_ : " . ${$self->$_} if( $self->$_) }  
      #                                       qw( parent next_sibling previous_sibling first_child)
      #             )
      #      . ' : '
             . join ( " - ", map { "$_ : " . $self->$_ }  (qw( name value pos)) )
             . " : " . join( " - ", map { $_->dump } @{$self->attributes})
             ;
    }
 
  sub dump_all
    { my $class= shift;
      foreach my $id (1..$last_obj)
        { my $self= bless \$id, $class;
          print $self->dump, "\n";
        }
    }
}
      
1; 

package attribute;

{
  my( @name, @value, @parent, @pos);
  my $last_obj=0;
  
  sub new
    { my( $class, $parent, $name, $value)= @_;
      
      my $id= $last_obj++;
      my $self= bless \$id, $class;
      
      $self->name(   $name  );
      $self->value(  $value );
      $self->parent( $parent);

      return $self;
    }

  BEGIN
    { foreach my $method ( qw( parent name value pos) )
        { no strict 'refs';
          *{$method}= sub { my $self= shift; 
                            if( @_) { ${$method}[$$self]= shift; } 
                            return ${$method}[$$self];
                          };
        }
    }
   
  sub dump { my $self= shift; return $self->name . " => " . $self->value . " (" . $self->pos . ")"; }

}

1;
      
      
