package CType::Ref;

use 5.6.0;
use strict;
use warnings;

use Carp;
use CType;

no warnings 'recursion';

our @ISA = qw/CType/;

my @refs;

sub new
  {
    my $this = shift;
    my $class = ref($this) || $this;
    my $kind = shift;
    my $name = shift;
    my $namespace = shift;

    my $self = {kind => $kind,
                name => $name,
                type => undef,
               };
    bless $self, $class;

    $self->do_complete($namespace) if $namespace;
    push @refs, $self unless $self->{type};

    return $self;
  }

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

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

sub complete_refs
  {
    my $namespace = shift;

    $_->do_complete($namespace) foreach @refs;
    @refs = ();
  }

sub do_complete
  {
    my $self = shift;
    my $namespace = shift;

    $self->{type} = $namespace->get($self->{kind}, $self->{name});
  }

sub complete
  {
    my $self = shift;
    return $self->{type} ? $self->{type}->complete : 0;
  }

sub best_type_for_comparison
  {
    my $self = shift;
    return $self unless $self->{type};
    if ($self->{kind} eq 'ordinary')
      {
        # We look through typedefs, so that we can compare 'int' and
        # 'foo', where 'typedef int foo'
        return $self->{type}->best_type_for_comparison;
      }
    else
      {
        # We don't look through anything else, because 'int' and
        # 'struct foo' are always a mismatch
        return $self;
      }
  }

sub type
  {
    my $self = shift;
    my $accept_incomplete = shift;

    unless ($self->{type})
      {
        return undef if $accept_incomplete;
        confess "$self->{kind} $self->{name} is incomplete";
      }
    return $self->{type}->type($accept_incomplete);
  }

sub describe
  {
    my $self = shift;
    my $qualifiers = $self->describe_qualifiers;
    $qualifiers .= ' ' if $qualifiers;

    my $str = $qualifiers;
    if ($self->{kind} eq 'ordinary')
      {
        $str .= $self->{name};
      }
    else
      {
        $str .= "$self->{kind} $self->{name}";
      }
    return $str;
  }

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

    my $qualifiers = $self->dump_c_qualifiers;
    my $str = '';
    if ($self->{kind} eq 'ordinary')
      {
        $str .= $self->{name};
      }
    else
      {
        $str .= "$self->{kind} $self->{name}";
      }
    $str .= ' ' . $qualifiers if $qualifiers;
    return $str;
  }

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

    # It's possible for us to get loops here, so we break cycles
    return if $self->{laying_out};
    $self->{laying_out} = 1;

    my $type = $self->type($accept_incomplete);
    $type->layout($accept_incomplete, $namespace) if $type;

    $self->{laying_out} = 0;
  }

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

    return 'both' unless $other->isa('CType::Ref');
    return 'both' if $self->{kind} ne $other->{kind};
    return 'both' if $self->{name} ne $other->{name};
    return 'ok';
  }

sub get_refs
  {
    my $self = shift;
    return ($self);
  }

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

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

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

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

1;
