use strict;
use warnings;

package Boilerplater::Symbol;
use Boilerplater::Parcel;
use Scalar::Util qw( blessed );
use Carp;

my %new_params = (
    parcel   => undef,
    exposure => undef,
);

sub new {
    my ( $either, %args ) = @_;

    # Acquire a Parcel.
    my $parcel = $args{parcel};
    if ( !defined $parcel ) {
        $parcel = Boilerplater::Parcel->default_parcel;
    }
    elsif ( blessed($parcel) ) {
        confess("Not a Boilerplater::Parcel")
            unless $parcel->isa('Boilerplater::Parcel');
    }
    else {
        $parcel = Boilerplater::Parcel->singleton( name => $args{parcel} );
    }

    # Create the object.
    my $self = bless { %new_params, %args, parcel => $parcel },
        ref($either) || $either;

    # Validate the symbol's exposure, then cache accessor values.
    confess("Invalid value for 'exposure': $self->{exposure}")
        unless $self->{exposure} =~ /^(?:public|parcel|private|local)$/;
    $self->{_public}  = $self->{exposure} eq 'public'  ? 1 : 0;
    $self->{_private} = $self->{exposure} eq 'private' ? 1 : 0;
    $self->{_parcel}  = $self->{exposure} eq 'parcel'  ? 1 : 0;
    $self->{_local}   = $self->{exposure} eq 'local'   ? 1 : 0;

    return $self;
}

sub set_parcel {
    my ( $self, $parcel ) = @_;
    if ( blessed($parcel) and $parcel->isa('Boilerplater::Parcel') ) {
        $self->{parcel} = $parcel;
    }
    else {
        $self->{parcel} = Boilerplater::Parcel->singleton( name => $parcel );
    }
}
sub get_parcel { shift->{parcel} }

sub get_prefix { shift->{parcel}->get_prefix; }
sub get_Prefix { shift->{parcel}->get_Prefix; }
sub get_PREFIX { shift->{parcel}->get_PREFIX; }

sub public  { shift->{_public} }
sub private { shift->{_private} }
sub parcel  { shift->{_parcel} }
sub local   { shift->{_local} }

sub equals {
    my ( $self, $other ) = @_;
    return 0 unless $self->{parcel}->equals( $other->{parcel} );
    if ( defined $self->{exposure} ) {
        return 0 unless defined $other->{exposure};
        return 0 unless $self->{exposure} eq $other->{exposure};
    }
    else {
        return 0 if defined $other->{exposure};
    }
    return 1;
}

1;

__END__

__POD__

=head1 NAME

Boilerplater::Symbol - Abstract base class for Boilerplater symbols.

=head1 DESCRIPTION

Boilerplater::Symbol serves as an abstract parent class for entities which may
live in the global namespace, such as classes, functions, methods, and
variables.

=head1 CONSTRUCTOR

    my $symbol = MySymbol->new(
        parcel   => $parcel,      # required
        exposure => $exposure,    # required
    );

=over

=item

B<parcel> - A Boilerplater::Parcel, or a string that can be used to
create/retrieve one.

=item

B<exposure> - The scope in which the symbol is exposed.  Must be 'public',
'parcel', 'private', or 'local'.

=back

=head1 OBJECT METHODS

=head2 get_parcel set_parcel

Accessors for C<parcel> member var.

=head2 get_prefix get_Prefix get_PREFIX

Get a string prefix, delegating to C<parcel> member var.

=head2 public parcel private local

    if    ( $sym->public ) { do_x() }
    elsif ( $sym->parcel ) { do_y() }

Indicate whether the symbol matches a given access level.

=head2 equals

    do_stuff() if $sym->equals($other_sym);

Returns true if the symbols are "equal", false otherwise.

=head1 COPYRIGHT

Copyright 2008-2009 Marvin Humphrey

=head1 LICENSE, DISCLAIMER, BUGS, etc.

See L<KinoSearch> version 0.30.

=cut