package CType::Pointer;

use 5.6.0;
use strict;
use warnings;

use CType;
use CType::Fundamental;

no warnings 'recursion';

our @ISA = qw/CType::Fundamental::VoidPtr/;

sub new
  {
    my $this = shift;
    my $class = ref($this) || $this;
    my $type = shift;
    my $qualifiers = shift;
    my $attributes = shift;

    my $const = 0;
    my $restrict = 0;
    my $volatile = 0;

    foreach my $qualifier (@$qualifiers)
      {
        if ($qualifier->isa('CParse::TypeQualifier'))
          {
            if ($qualifier->name eq 'const')
              {
                $const = 1;
              }
            elsif ($qualifier->name eq 'volatile')
              {
                $volatile = 1;
              }
            elsif ($qualifier->name eq 'restrict')
              {
                $restrict = 1;
              }
            else
              {
                die "Internal error: unknown type qualifier " . $qualifier->name;
              }
          }
        else
          {
            die "Internal error: unknown type qualifier $qualifier";
          }
      }

    my $self = {type => $type,
                attributes => $attributes,
                const => $const,
                restrict => $restrict,
                volatile => $volatile,
               };
    bless $self, $class;

    $self->process_attributes($attributes);

    return $self;
  }

sub describe
  {
    my $self = shift;

    my $qualifiers = $self->describe_qualifiers;
    $qualifiers .= ' ' if $qualifiers;

    my $type = $self->{type}->describe;
    return $qualifiers . "pointer to " . $type;
  }

sub dump_c
  {
    my $self = shift;
    my $skip_cpp = shift;
    my $name = shift;

    my $qualifiers = $self->dump_c_qualifiers;

    my $str = '';
    $str .= '*';
    $str .= ' ' . $qualifiers if $qualifiers;
    $str .= ' ' . $name if $name;

    if ($self->{type}->capture_declarator)
      {
        unless ($self->{type}->isa('CType::Pointer'))
          {
            # Anything that captures, and isn't a pointer, needs to be
            # protected with parentheses
            $str = "($str)";
          }
        $str = $self->{type}->dump_c($skip_cpp, $str);
      }
    else
      {
        $str = $self->{type}->dump_c($skip_cpp) . ' ' . $str;
      }

    return $str;
  }

sub capture_declarator
  {
    return 1;
  }

sub _check_interface
  {
    my $self = shift;
    my $other = shift;

    return 'both' unless $other->isa('CType::Pointer');

    my @ret;

    push @ret, $self->check_sizes($other);
    push @ret, $self->check_qualifiers($other);
    push @ret, $self->{type}->check_interface($other->{type});

    return @ret;
  }

sub get_refs
  {
    my $self = shift;
    return $self->{type}->get_refs;
  }

sub layout
  {
    my $self = shift;
    my $accept_incomplete = shift;
    my $namespace = shift;

    $accept_incomplete = 1;

    $self->{type}->layout($accept_incomplete, $namespace);
  }

1;
