package CodeCounter;

use version; $VERSION = qv('0.0.1');

use warnings;
use strict;
use Carp;

use base qw( Exporter );

use CodeCounter::Standards;

our @EXPORT = qw( base_count );

sub base_count{
    my ($dir, $standards_file) = @_;
    my %file_counts;
    my $standards = load_standards($standards_file);
    
    directory_and_file_iter($dir, make_code_counter($standards, \%file_counts));
    if(wantarray){
        return %file_counts;
    }
    else{
        my $total = 0;
        foreach my $count (values %file_counts){
            $total+=$count;
        }
        return $total;
    }
}

sub make_code_counter{
    my ($standards, $file_counts) = @_;

    my $ret = sub{
        my($file) = @_;
        my ($count, $ext, $rules, $in);
        
        my @exts = ($file =~ /\.([^.]+)$/mg);
        $ext = $exts[@exts-1];
        if (not exists $standards->{$ext}){
            warn "Unknown file extention: $ext.  Skipping...\n";
            $file_counts->{$file} = 0;
            return;
        }
        
        $rules = $standards->{$ext};
        
        open $in, "<", $file;
        my @lines = <$in>;
        foreach my $line (@lines){
            my $result;
            
            $line =~ s/^\s+//;
            $line =~ s/\s+$//;
            
            for my $rule (keys %{$rules}){ 
                $result = $rules->{$rule}->($line);
                last if $result ne "pass";
            }
            $count++ if $result eq 'pass';
            last if $result eq 'stop';
        }
        
        close $in;
        $file_counts->{$file} = $count;
    };
    return $ret;
}

sub directory_and_file_iter{
    my ($dir, $agent) = @_;
    if    (-d $dir){
        my @files = glob "$dir/*";
        foreach my $file (@files){
            directory_and_file_iter( $file, $agent);
        }
    }
    elsif (-f $dir){
        $agent->($dir);
    }
    else{
        carp "$dir is of unknown type $!\n";
    }
}

1; # Magic true value required at end of module
__END__

=head1 NAME

CodeCounter - A basic source lines of code counter shell.


=head1 VERSION

This document describes CodeCounter version 0.0.1


=head1 SYNOPSIS

    use CodeCounter;
    use Data::Dumper;
    
    my %results = base_count('t/test-code', 't/standards.xml');
    print Dumper(%results); # prints off file names and LOC counts for each file
    
    # --OR--
    
    my $base_count = = base_count('t/test-code', 't/standards.xml');
    print "SLOC: $base_count\n"; #prints accumulated LOC for countable source files
      
  
=head1 DESCRIPTION

    Provides a basic framework for SLOC counting using standards provided by CodeCounter::Standards.
    It currently works based on directory walking, working with files that it has extentions for
    and performing counts.  Files having extentions that are unknown aren't counted.

=head1 INTERFACE 

=head2 hash base_count(source_code_directory, standard_file_path)

    This is the exported interface from this module.  It takes a source directory
    and the path to the standards file.  The standards are opened and the rules
    contained in the standards are applied against all known files in the 
    source_code_directory.

=head2 directory_and_file_iter(standard_file_path, callback)

    A simple directory walker where the callback is passed each file in found
    during the iteration.

=head2 sub make_code_counter(standard_ref, hash_of_files_and_counts_ref)

    This function creates the actual code counter that is passed as an agent
    into directory_and_file_iter by the base_count function.  The produced
    function does the real counting work.

=head1 DIAGNOSTICS

    This is still a work in progress.  Many possible errors will be passed
    by the libraries that this module is built upon.

=over

=item C<< <file_or_directory> is of unknown type <error location>\n >>

    Not sure when this would happend when counting source code, but...

=item C<< "Unknown file extention: <extention_without_associated_standard>.  Skipping...\n" >>

    This error will pop up if a file extention that isn't known is encountered.
    In many cases this is what you want, in others you'll need to add the extention
    to an existing standard or create a new one.

=back


=head1 CONFIGURATION AND ENVIRONMENT

CodeCounter requires no configuration files or environment variables.


=head1 DEPENDENCIES

CodeCounter::Standards


=head1 INCOMPATIBILITIES

None reported.


=head1 BUGS AND LIMITATIONS

    This module is capable of producing a base count, but cannot compare two
    existing baselines today.  In the next iteration you will be able to 
    compare baselines, which will produce size differences in between versions.
    For legacy code, this is the only counting method to determine size for projects.

Please report any bugs or feature requests to
C<bug-codecounter@rt.cpan.org>, or through the web interface at
L<http://rt.cpan.org>.

=head1 AUTHOR

John Fraser  C<< <raptnor@yahoo.com> >>


=head1 LICENCE AND COPYRIGHT

Copyright (c) 2005, John Fraser C<< <raptnor@yahoo.com> >>. All rights reserved.

This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself. See L<perlartistic>.


=head1 DISCLAIMER OF WARRANTY

BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
NECESSARY SERVICING, REPAIR, OR CORRECTION.

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
