package Sisimai::Group;
use feature ':5.10';
use strict;
use warnings;
use Try::Tiny;
use Module::Load;
use Sisimai::Group::Phone;
use Sisimai::Group::Web;

sub find {
    # @Description  Find a category name and a provider name
    # @Param <data> (Hash) Email address of the recipient, callbacks
    # @Return       (Ref->Hash) Data(category,provider)
    my $class = shift;
    my $argvs = { @_ };
    my $group = { 'category' => '', 'provider' => '' };

    my $recipient1 = $argvs->{'email'} // '';
    my $callbackto = {
        'category' => $argvs->{'category'} // sub {},
        'provider' => $argvs->{'provider'} // sub {},
    };
    return undef unless length $recipient1;
    require Sisimai::RFC2606;

    my $domainpart = [ split( '@', $recipient1 ) ]->[-1];
    my $cctldomain = [ split( /[.]/, $domainpart ) ]->[-1];
    my $iso3166txt = '';
    my $categories = [ 'Web', 'Phone' ];
    my $groupclass = '';
    my $raprovider = '';

    # Hard coded order, the following order is sorted by the size of directory,
    # is generated by the following command:
    # for D in `find lib/Sisimai/Group -type d -name '??'`; do
    #   C=`basename $D`
    #   echo -n $C
    #   wc -m $D/* | grep total
    # done | sort -k2 -gr
    my $cctldorder = [ qw|
        US JP AU UK EG CA IN DE AR BR ZA ES NZ RU FR TW CZ SG RO NO IL CN TH AL
        PT VN SK AE HK CO AT NP NL PR CH MX IE IT PA BG SE PL JM LU NI DK BE CL
        ID UY DO EC SV PH KE VE IS GT SA MO LB MA UG KR GR TR PE LK OM MY HR AW
        CR RS BS BM MU PK NG SR MK HU LV ME HN PY UA MD IR 
    | ];        

    if( $domainpart eq 'localhost.localdomain' ) {
        # localhost.localdomain
        return { 'category' => 'pc', 'provider' => 'local' };

    } elsif( Sisimai::RFC2606->is_reserved( $domainpart ) ) {
        # Reserved domain in RFC2606
        return { 'category' => 'reserved', 'provider' => 'ietf' };
    }

    CATEGORY:while(1) {
        # Find a category name and a provider name
        for my $e ( @$categories ) {
            # Check by find() method in Sisimai::Group::{Phone,Web}
            $groupclass = 'Sisimai::Group::'.$e;
            $raprovider = $groupclass->find( $domainpart );
            last if length $raprovider;
        }
        last(CATEGORY) if length $raprovider;

        # Check the top level domain part of the domain part
        if( length $cctldomain == 2 ) {
            # CCTLD of the domain is 2 characters
            require Sisimai::ISO3166;
            $iso3166txt = Sisimai::ISO3166->get( $cctldomain, 'alpha-2' );

            TLD: for my $e ( reverse @$categories ) {
                # Look for a category name and a provider name
                try {
                    # Try to ::Phone->find, ::Web->find
                    $groupclass = sprintf( "Sisimai::Group::%s::%s", $iso3166txt, $e );
                    Module::Load::load( $groupclass );
                    $raprovider = $groupclass->find( $domainpart );

                } catch {
                    ;
                };
                last(TLD) if length $raprovider;
            }
        }
        last(CATEGORY) if length $raprovider;

        # Check user defined module?

        # Check ::Web, ::Phone in each module in Sisimai::Group::<CCTLD>
        CCTLD: for my $e ( @$cctldorder ) {
            # Skip if the CCTLD is equal to the top level domain of the domain
            # part.
            next if $e eq $iso3166txt;

            CATEGORY: for my $f ( reverse @$categories ) {
                # Check Phone.pm -> Web.pm
                try {
                    # Try to ::Phone->find, ::Web->find
                    $groupclass = sprintf( "Sisimai::Group::%s::%s", $e, $f );
                    Module::Load::load( $groupclass );

                    $raprovider = $groupclass->find( $domainpart );

                } catch {
                    next;
                };
                last(CCTLD) if length $raprovider
            }
        }
        last;
    }

    $group->{'provider'} = $raprovider // '';
    $group->{'category'} = $raprovider ? $groupclass->category : 'pc';
    $group->{'provider'} = 'others' unless length $group->{'provider'};
    return $group;
}

1;
__END__

=encoding utf-8

=head1 NAME

Sisimai::Group - Classification utility class

=head1 SYNOPSIS

    use Sisimai::Group;

    my $e = '*******@gmail.com';
    my $v = Sisimai::Group->find( 'email' => $e );
    warn Dumper $v;
    $VAR1 = {
        'category' => 'web',
        'provider' => 'google'
    }


=head1 DESCRIPTION

Sisimai::Group find a category name and a provider name for classification.

=head1 CLASS METHODS

=head2 C<B<find( I<Email address> )>>

C<find()> returns hash reference which include the provider name and a category
name of the email address like following:

    my $e = '*******@verizon.net';
    my $v = Sisimai::Group->find( 'email' => $e );
    warn Dumper $v;
    $VAR1 = {
        'category' => 'phone',
        'provider' => 'verizon'
    }

=head1 AUTHOR

azumakuniyuki

=head1 COPYRIGHT

Copyright (C) 2014 azumakuniyuki E<lt>perl.org@azumakuniyuki.orgE<gt>,
All Rights Reserved.

=head1 LICENSE

This software is distributed under The BSD 2-Clause License.

=cut
