
#------------------------------------------------------------
# Copyright (C) 2007 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------

#------------------------------------------------------------
# Validation utility class containg random exported functions
#------------------------------------------------------------
package Validate::Util;

use base Exporter;

use lib '/app/tools/rps/lib';
use RPS::CountryList;
use RPS::DB::Item::Genre;

use Business::Barcode::EAN13 qw( valid_barcode );
use Business::UPC;

@EXPORT    = qw( );
@EXPORT_OK = qw( is_integer
  is_valid_territory_list
  is_valid_genre
  is_valid_isrc
  is_valid_upc_or_ean
  is_sequence
  calculate_duration );

#------------------------------------------------------------
# bool is_integer( $target )
#
# Determine if a scalar represents a valid interger.
#
# Parameters:
#   $target - scalar containing a string to test.
#
# Return:
#   bool - true if a valid integer, otherwise undef
#------------------------------------------------------------
sub is_integer {
    my $target = shift || return 1;

    return undef unless ( defined($target) && !ref($target) );

    return $target =~ /^-?\d+$/;
}

#------------------------------------------------------------
# bool is_valid_territory_list
#
# Determine if a string contains a valid territory list which
# consists of two letter country codes delimited by
# whitespace.
#
# Parameters:
#   $target - scalar containing a string to test.
#
# Return:
#   bool - true if a valid list, otherwise undef
#------------------------------------------------------------
sub is_valid_territory_list {
    my $territory = shift || return 1;

    return invalid_territory_list($territory) ? undef : 1;
}

sub invalid_territory_list {
    my $territory = shift || return 1;
    my @invalid;

    return undef unless ( defined($territory) );

    my @countries = split( /\s+/, $territory );

    foreach my $country (@countries) {
        push( @invalid, $country )
          unless ( RPS::CountryList::CountryNameFor($country) );
    }

    return @invalid;
}

#------------------------------------------------------------
# bool is_valid_genre
#
# Determine if a target string is a valid genre via a DB
# lookup.
#
# Parameters:
#   $target - scalar containing a string to test.
#
# Return:
#   bool - true if a valid list, otherwise undef
#------------------------------------------------------------
sub is_valid_genre {
    my $genre = shift || return 1;

    return undef unless ( defined($genre) );

    my $genreDBItemObj = RPS::DB::Item::Genre->Lookup( genre_name => $genre );

    return $genreDBItemObj ? 1 : undef;
}

#------------------------------------------------------------
# bool is_valid_isrc
#
# Determine if a target string is a valid isrc code
#
# Scheme found at:
#
#  http://en.wikipedia.org/wiki/International_Standard_Recording_Code
#
# Parameters:
#   $target - scalar containing a string to test.
#
# Return:
#   bool - true if a valid list, otherwise undef
#------------------------------------------------------------
sub is_valid_isrc {
    my $isrc = shift || return 1;

    return undef unless ( defined($isrc) );

    $isrc =~ /^(..)(...)(..)(.*)/;
    my ( $country_code, $company, $year, $id ) = ( $1, $2, $3, $4, $5 );

    return undef unless ( defined($country_code)
        && defined($company)
        && defined($year)
        && defined($id) );

    $country_code = uc($country_code);

    # to accomodate country codes that have been deleted (whether by merging,
    # changing, whatever), codes that are reserved for special purposes
    # and may have been appropriated for ISRCs, *and* perhaps most importantly,
    # mistakes that become facts, we'll just check them for letterness and
    # call it good.
    #
    # we had to do this in FB13995 for QM and it came up again in FB15394 for
    # 'YD',  which was Yemen, Democratic until 1990 (and now merged into Yemen).
    # The second being a good example of it being entirely possible to record
    # music there in the late 80's and it doesn't go away just because the
    # country does. I'm sure this isn't the only place in the system where we
    # should accomodate this, but you gots to start somewhere.
    #
    # and finally, i'll admit this is much more of a comment than such a thing
    # deserves...

    return undef unless ( $country_code =~ /^[A-Z]{2}$/ );
    return undef unless ( $company =~ /^[\d\w]{3}$/ );
    return undef unless ( $year =~ /^\d{2}$/ );
    return undef unless ( $id =~ /^\d{5}$/ );

    return 1;
}

#------------------------------------------------------------
# bool is_valid_upc_or_ean
#
# Determine if a string represents a valid UPC-a or EAN13
# code with a valid check digit.
#
# Parameters:
#   $target - scalar containing a string to test.
#
# Return:
#   bool - true if a valid list, otherwise undef
#------------------------------------------------------------
sub is_valid_upc_or_ean {
    my $barcode = shift || return 1;

    return undef unless ( defined($barcode) );

    if ( length($barcode) == 12 ) {
        my $upc = new Business::UPC($barcode) || return undef;
        return $upc->is_valid();
    } elsif ( length($barcode) == 13 ) {
        return valid_barcode($barcode);
    } else {
        return undef;
    }

    return undef;
}

#------------------------------------------------------------
# bool is_sequence
#
# Determine if a list of integers is in sequence with no
# skips or duplicates.
#
# Parameters:
#   list      - list of integers
#   minimum   - the starting point for the sequence
#   maximum   - the ending point of the sequence
#   deviation - standard deviation for the list (default: 1)
#
# Return:
#   bool - true if a valid list, otherwise undef
#------------------------------------------------------------
sub is_sequence {
    my %args = @_;
    my $deviation = defined( $args{deviation} ) ? $args{deviation} : 1;

    die "list is required and must be a list reference"
      unless ( $args{list} && ref( $args{list} ) eq 'ARRAY' );

    my @data = sort { $a <=> $b } @{ $args{list} };

    return undef unless (@data);

    return undef if ( defined( $args{minimum} ) && $args{minimum} != $data[0] );
    return undef if ( defined( $args{maximum} ) && $args{maximum} != $data[ @data - 1 ] );

    for ( my $i = 0 ; $i < @data - 1 ; $i++ ) {
        return undef unless ( is_integer( $data[$i] ) && is_integer( $data[ $i + 1 ] ) );
        return undef unless ( $data[$i] == $data[ $i + 1 ] - $deviation );
    }

    return 1;
}

sub is_valid_composer {
    my $target = shift;
    my %args   = @_;

    if (   RPS::DB::Item::Genre::isComposerRequired( genre => $args{primary_genre} )
        || RPS::DB::Item::Genre::isComposerRequired( genre => $args{secondary_genre} ) ) {
        return undef unless ( defined($target) && length($target) );
    }

    return 1;
}

sub calculate_duration {
    my %args = @_;

    my $min = $args{minutes};
    my $sec = $args{seconds};

    if ( $min && $min =~ /:/ ) {
        ( $min, $sec ) = split( /:/, $min );
    }

    # If we don't have minutes OR seconds, return undef (fail)
    return undef unless ( defined($min) || defined($sec) );

    $min = defined($min) ? $min : 0;
    $sec = defined($sec) ? $sec : 0;

    return $min * 60 + $sec;
}
1;
