package ASRemote::Variable;
use strict;
use vars qw( $VERSION );

###############################################################################
# Get our version number from the CVS revision number.
###############################################################################
$VERSION = do { my @r = q$Revision: 1.1 $ =~ /\d+/g; sprintf '%d.' . '%02d'x$#r, @r };

###############################################################################
# Subroutine:   new ($string)
###############################################################################
# Creates a new instance of a ASRemote::Variable and parses our values out of
# $string.  If able to parse the values properly, this method returns a
# reference to a new ASRemote::Variable object.  If unable to parse the values
# properly, this method returns undef.
###############################################################################
sub new
{
    my $class = shift;
    my ($var) = @_;
    my $self = {};

    if ($var =~ /^\s*ASRemote::(\S+) +(.+) +(.*)\n\0\n/sm) {
        my $leftover = $';
        bless $self, $class;

        # Stuff the name and scalar values we just parsed into ourselves.
        $self->{ 'type' }   = $1;
        $self->{ 'name' }   = $2;
        $self->{ 'scalar' } = $3;

        return $self;
    }
    return undef;
}

###############################################################################
# Subroutine:   expand_from ($src, @expansion)
# Parameters:   $src        - Source variable to expand items from
#               @expansion  - List of expansions to perform
###############################################################################
# Expands a set of sub-elements from the $src variable, copying them into our
# own structure.  The list of expansions to be performed comes from @expansion;
# a comma-separated list of the 'name' properties to expand and copy over.
###############################################################################
sub expand_from ($@)
{
    my ($self, $src, @expansion) = @_;
    my ($dst, $exp);
    my $roottype = shift @expansion;
    my $rootname = shift @expansion;

    if (scalar @expansion > 0)
    {
        foreach $exp (@expansion)
        {
            my $dst;

            # Find the element to expand in our own list of sub-elements
            foreach my $key (@{$self->{'list'}})
            {
                if ($key->{'name'} eq $exp)
                    { $dst = $key; }
            }

            # Find the element to expand from in the source variable
            my $srcelem;
            foreach my $key (@{$src->{'list'}})
            {
                if ($key->{'name'} eq $exp)
                    { $srcelem = $key; }
            }

            # Clone the sub-element over.
            foreach my $elem (@{$srcelem->{'list'}})
            {
                push( @{$dst->{'list'}}, $elem->clone() );
            }
        }
    }
    else
    {
        $self->{'list'} = [];
        foreach my $elem (@{$src->{'list'}})
        {
            push( @{$self->{'list'}}, $elem->clone() );
        }
    }
}

###############################################################################
# Subroutine:   clone ()
###############################################################################
# Clones a copy of the ASRemote::Variable object EXCEPT FOR IT'S LIST CONTEXT.
# Returns a new ASRemote::Variable object to the caller.
###############################################################################
sub clone ()
{
    my $self = shift;
    my $dst;
    foreach my $key (keys %{$self})
    {
        if (ref $self->{$key})
            { $dst->{$key} = undef; }
        else
            { $dst->{$key} = $self->{$key}; }
    }
    bless $dst, ref $self;
    return $dst;
}

###############################################################################
# Subroutine:   as_string
###############################################################################
# Returns a string value containing the information which this variable
# contains.  This string value can then be used for outputting of the value to
# the user (if you choose).
###############################################################################
sub as_string
{
    my ($self, $indent) = @_;
    my $rtnval;

    $rtnval  = ' ' x $indent .
               $self->{ 'name' } .
               ' -> ' .
               $self->{ 'scalar' };

    if (defined $self->{'expandable'})
        { $rtnval .= ' (' . $self->{'expandable'} . ')'; }

    $rtnval .= "\n";

    foreach my $kid (@{$self->{ 'list' }})
        { $rtnval .= $kid->as_string( $indent + 3 ); }

    return $rtnval;
}

1;
__END__;

=head1 NAME

ASRemote::Variable - Watch/proximity variable structure for ASRemote

=head1 SYNOPSIS

 use ASRemote::Variable;
 ...
 my $var = new ASRemote::Variable( $string );

=head1 DESCRIPTION

The C<ASRemote::Variable> module encapsulates all of the functionality required for
parsing watch and proximity variables that are received back from the remote
debugger.  C<ASRemote::Variable> will parse the entire string sent by by the remote
debugger and stuff all of the values into it's own structure.  This structure
can then be traversed to get information as to what the original variable
looked like.

=head1 STRUCTURE

C<ASRemote::Variable> is primarily a hash reference, and all of it's element should
be accessed directly whenever possible (sorry).  As such, it's structure is
unlikely to change except to add more properties whenever they are needed.  The
following properties are available in C<ASRemote::Variable>:

=over 4

=item name

The name of the variable (e.g. C<$foobar>).  In the case of child variables
(e.g. array elements), the name of the variable will be the offset or hash key
for that individual element (e.g. C<'key'>), B<not> the full name of the
variable (e.g. C<$foobar{'key'}>).

=item expandable

A piece of Perl code which can be passed to C<ASRemote::Jacket> to tell it to
expand or contract expansion of this variable.  In the case of scalar values
(which are not expandable) or empty hash/list value, this value will be
C<undef>.

=item scalar

The value of the variable when taken in a scalar context.

=item list

A list of any child variables that this variable may have.  Each of the
elements of this list are also blessed into the C<ASRemote::Variable> package,
although they are B<not> created through the call to 'C<new>' (we build them
directly).  Note that in the case of scalar values or unexpanded lists/hashes,
this value will be C<undef>.

=back

=head1 METHODS

=over 4

=item new ($string)

Creates a new C<ASRemote::Variable> object, parsing the values we're supposed to
contain from C<$string>.  If able to parse the value properly, this method will
return a reference to a new C<ASRemote::Variable> object.  If unable to parse the
value properly, this method returns C<undef>.

The format of the string that we are to be parsing is as follows:

 ASRemote::<type> <variable_name> <scalar_value>
 (
    <list_value>
 )

Examples:

 ASRemote::Watch $DB::single 0
 (
 )

 ASRemote::Proximity @foolist 3
 (
 0  'abc'
 1  'def'
 2  'ghi'
 )

 ASRemote::Watch %foohash 2/8
 (
 'key'  => 'value'
 'newlist'  => ARRAY(0x123456)
    0   'abc'
    1   'def'
 'newkey'   => 'newvalue'
 )

=item as_string

Returns a string value containing a text representation of the
C<ASRemote::Variable> object.  This method can be used to get the information
contained within the object in a format suitable for output to the user.

=back

=head1 KNOWN BUGS / LIMITATIONS

Currently, C<ASRemote::Variable> assumes that everything that comes back from the
remote debugger has an indentation of B<4> characters for any sub-element or
child variable.

=head1 AUTHOR

Graham TerMarsch <grahamt@activestate.com>

Doug Lankshear <dougl@activestate.com>

Dick Hardt <dickh@activestate.com>

=cut
