package CParse::Declarator::Function;

use 5.6.0;
use strict;
use warnings;

use CDecl;
use CType::Function;

sub new
  {
    my $this = shift;
    my $class = ref($this) || $this;
    my $params = shift;
    my $variadic = shift;

    my $self = {declarator => undef,
                params => $params,
                variadic => $variadic,
               };

    if (scalar @{$self->{params}} == 1
        and scalar @{$self->{params}[0]->specifiers} == 1
        and $self->{params}[0]->specifiers->[0]->isa('CParse::TypeSpecifier')
        and $self->{params}[0]->specifiers->[0]->name eq 'void')
      {
        # We have precisely one argument, which is just a single
        # 'void'. That's really 'no arguments'.
        $self->{params} = [];
      }

    bless $self, $class;
    return $self;
  }

sub set_declarator
  {
    my $self = shift;
    $self->{declarator} = shift;
  }

sub dump_c
  {
    my $self = shift;
    my $declarator = $self->{declarator} ? $self->{declarator}->dump_c : '';
    my $params = join(', ', map {$_->dump_c} @{$self->{params}});
    if ($self->{variadic})
      {
        if (length $params)
          {
            $params .= ", ...";
          }
        else
          {
            $params = "...";
          }
      }

    unless (length $params)
      {
        $params = 'void';
      }

    if ($self->{declarator} and $self->{declarator}->isa('CParse::Declarator') and $self->{declarator}->pointer)
      {
        return "($declarator)($params)"
      }
    else
      {
        return "$declarator($params)"
      }
  }

sub get_identifier
  {
    my $self = shift;

    return undef unless $self->{declarator};
    return $self->{declarator}->get_identifier;
  }

sub get_type
  {
    my $self = shift;

    my $namespace = shift;
    my $base_type = shift;
    my $attributes = shift;

    my @arg_types = map {new CDecl 'param', $_->get_identifier, $_->get_type($namespace)} @{$self->{params}};

    my $type = new CType::Function $base_type, \@arg_types, $attributes, $self->{variadic};
    if ($self->{declarator})
      {
        $type = $self->{declarator}->get_type($namespace, $type, []);
      }
    return $type;
  }

1;
