package ExtUtils::XSBuilder::FunctionMap;

use strict;
use warnings FATAL => 'all';
use ExtUtils::XSBuilder::MapUtil qw(function_table structure_table);
use Data::Dumper ;

our @ISA = qw(ExtUtils::XSBuilder::MapBase);

sub new {
    my $class = shift;
    bless {wrapxs => shift}, $class;
}

#for adding to function.map
sub generate {
    my $self = shift;

    my $missing = $self->check;
    return unless $missing;

    print " $_\n" for @$missing;
}

sub disabled { shift->{disabled} }

#look for functions that do not exist in *.map
sub check {
    my $self = shift;
    my $map = $self->get;

    my @missing;
    my $parsesource = $self -> {wrapxs} -> parsesource_objects ;

    loop:
    for my $name (map $_->{name}, @{ function_table($self -> {wrapxs}) }) {
        next if exists $map->{$name};
        #foreach my $obj (@$parsesource)
        #    {
        #    next loop if ($obj -> handle_func ($name)) ;
        #    }
        push @missing, $name ;
    }

    return @missing ? \@missing : undef;
}

#look for functions in *.map that do not exist
my $special_name = qr{(^DEFINE_|DESTROY$)};

sub check_exists {
    my $self = shift;

    my %functions = map { $_->{name}, 1 } @{ function_table($self -> {wrapxs}) };
    my @missing = ();

    for my $name (keys %{ $self->{map} }) {
        next if $functions{$name};
        push @missing, $name unless $name =~ $special_name;
    }

    return @missing ? \@missing : undef;
}

my $keywords = join '|', qw(MODULE PACKAGE PREFIX BOOT);



sub class_c_prefix {
    my $self = shift;
    my $class = shift;
    $class =~ s/:/_/g;
    $class;
}

sub class_xs_prefix {
    my $self = shift;
    my $class = shift;
    my $class_prefix = $self -> class_c_prefix($class);
    return $self -> {wrapxs} -> my_xs_prefix . $class_prefix . '_' ;
}

sub needs_prefix {
    my $self = shift;
    my $name = shift;
    $self -> {wrapxs} -> needs_prefix ($name) ;
}

sub make_prefix {
    my($self, $name, $class) = @_;
    my $class_prefix = $self -> class_xs_prefix($class);
    return $name if $name =~ /^$class_prefix/;
    $class_prefix . $name;
}


sub guess_prefix {
    my $self = shift;
    my $entry = shift;

    my($name, $class) = ($entry->{name}, $entry->{class});
    my $prefix = "";
    my $myprefix = $self -> {wrapxs} -> my_xs_prefix ;
    $name =~ s/^DEFINE_//;
    $name =~ s/^$myprefix//i;

    (my $guess = lc($entry->{class} || $entry->{module}) . '_') =~ s/::/_/g;
    $guess =~ s/(apache)_/($1|ap)_{1,2}/;

    if ($name =~ s/^($guess).*/$1/i) {
        $prefix = $1;
    }
    else {
        if ($name =~ /^(apr?_)/) {
            $prefix = $1;
        }
    }

    #print "GUESS prefix=$guess, name=$entry->{name} -> $prefix\n";

    return $prefix;
}

sub parse {
    my($self, $fh, $map) = @_;
    my %cur;
    my $disabled = 0;

    while ($fh->readline) {
        if (/($keywords)=/o) {
            $disabled = s/^\W//; #module is disabled
            my %words = $self->parse_keywords($_);

            if ($words{MODULE}) {
                %cur = ();
            }

            if ($words{PACKAGE}) {
                delete $cur{CLASS};
            }

            for (keys %words) {
                $cur{$_} = $words{$_};
            }

            next;
        }

        my($name, $dispatch, $argspec, $alias) = split /\s*\|\s*/;

        my $dispatch_argspec = '' ; 

        if ($dispatch && ($dispatch =~ m#\s*(.*?)\s*\((.*)\)#))
            {
            $dispatch = $1; 
            $dispatch_argspec = $2; 
            }

        my $return_type;

        if ($name =~ s/^([^:]+)://) {
            $return_type = $1;
        }

        if ($name =~ s/^(\W)// or not $cur{MODULE} or $disabled) {
            #notimplemented or cooked by hand
            $map->{$name} = undef;
            push @{ $self->{disabled}->{ $1 || '!' } }, $name;
            next;
        }

        if (my $package = $cur{PACKAGE}) {
            unless ($package eq 'guess') {
                $cur{CLASS} = $package;
            }
            if ($cur{ISA}) {
                $self->{isa}->{ $cur{MODULE} }->{$package} = delete $cur{ISA};
            }
            if ($cur{BOOT}) {
                $self->{boot}->{ $cur{MODULE} } = delete $cur{BOOT};
            }
        }
        else {
            $cur{CLASS} = $cur{MODULE};
        }

        if ($name =~ /^DEFINE_/ and $cur{CLASS}) {
            $name =~ s{^(DEFINE_)(.*)}
              {$1 . $self->make_prefix($2, $cur{CLASS})}e;
        print "DEFINE $name arg=$argspec\n" ;
	}

        my $entry = $map->{$name} = {
           name        => $alias || $name,
           dispatch    => $dispatch,
           dispatch_argspec    => $dispatch_argspec,
           argspec     => $argspec ? [split /\s*,\s*/, $argspec] : "",
           return_type => $return_type,
           alias       => $alias,
        };

        for (keys %cur) {
            $entry->{lc $_} = $cur{$_};
        }

        #avoid 'use of uninitialized value' warnings
        $entry->{$_} ||= "" for keys %{ $entry };
        if ($entry->{dispatch} =~ /_$/) {
            $entry->{dispatch} .= $name;
        }
    }
}

sub get {
    my $self = shift;

    $self->{map} ||= $self->parse_map_files;
}

sub prefixes {
    my $self = shift;
    $self = ExtUtils::XSBuilder::FunctionMap->new unless ref $self;

    my $map = $self->get;
    my %prefix;

    while (my($name, $ent) = each %$map) {
        next unless $ent->{prefix};
        $prefix{ $ent->{prefix} }++;
    }

    $prefix{$_} = 1 for qw(ap_ apr_); #make sure we get these

    [keys %prefix]
}


sub write {
    my ($self, $fh, $newentries, $prefix) = @_ ;

    foreach (@$newentries)
        {
        $fh -> print ($prefix, $self -> {wrapxs} -> mapline_func ($_), "\n") ;
        }
    }

1;
__END__