use strict;
use warnings;

package Clownfish::Type::Arbitrary;
use base qw( Clownfish::Type );
use Clownfish::Util qw( verify_args );
use Scalar::Util qw( blessed );
use Carp;

our %new_PARAMS = (
    parcel    => undef,
    specifier => undef,
);

sub new {
    my $either = shift;
    verify_args( \%new_PARAMS, @_ ) or confess $@;
    my $self = $either->SUPER::new(@_);

    # Validate specifier.
    confess("illegal specifier: '$self->{specifier}")
        unless $self->{specifier} =~ /^\w+$/;

    if ( $self->{specifier} =~ /^[A-Z]/ and $self->{parcel} ) {
        my $prefix = $self->{parcel}->get_prefix;
        # Add $prefix to what appear to be namespaced types.
        $self->{specifier} = $prefix . $self->{specifier}
            unless $self->{specifier} =~ /^$prefix/;
    }

    # Cache C representation.
    my $string = $self->const ? 'const ' : '';
    $string .= $self->{specifier};
    $self->set_c_string($string);

    return $self;
}

sub equals {
    my ( $self, $other ) = @_;
    return 0 unless blessed($other);
    return 0 unless $other->isa(__PACKAGE__);
    return 0 unless $self->{specifier} eq $other->{specifier};
    return 1;
}

1;

__END__

__POD__

=head1 NAME

Clownfish::Type::Arbitrary - An arbitrary type.

=head1 DESCRIPTION

The "arbitrary" type class is a hack that spares us from having to support C
types with complex declaration syntaxes -- such as unions, structs, enums, or
function pointers -- from within Clownfish itself.

The only constraint is that the C<specifier> must end in "_t".  This allows us
to create complex types in a C header file...

    typedef union { float f; int i; } floatint_t;

... pound-include the C header, then use the resulting typedef in a Clownfish
header file and have it parse as an "arbitrary" type.

    floatint_t floatint;

=head1 METHODS

=head2 new

    my $type = Clownfish::Type->new(
        specifier => 'floatint_t',    # required
        parcel    => 'Crustacean',    # default: undef
    );

=over

=item * B<specifier> - The name of the type, which must end in "_t".

=item * B<parcel> - A L<Clownfish::Parcel> or a parcel name.

=back

If C<parcel> is supplied and C<specifier> begins with a capital letter, the
Parcel's prefix will be prepended to the specifier:

    foo_t         -> foo_t                # no prefix prepending
    Lobster_foo_t -> crust_Lobster_foo_t  # prefix prepended

=head1 COPYRIGHT AND LICENSE

Copyright 2008-2011 Marvin Humphrey

This program is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.

=cut
