#------------------------------------------------------------
# Copyright (C) 2008 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------
use strict;

package Common::Locale;

use lib '/app/tools/common/lib';
use Common::RSApp;
use Common::Assert;
use Common::Consts;
use Common::XMLObject;
use Common::NumericFormat;
use Common::CurrencyFormat;
use Common::DateTimeFormat;
use Common::RSMath;

use base 'Common::FormObject';

# !!! I'd like to move the CODE_TO_COUNTRY map into here as well...
#
my %CODE_TO_LANGUAGE = (
    'en' => 'English',
    'fr' => 'French',
    'gr' => 'German',
    'es' => 'Spanish',
    'it' => 'Italian',
    'el' => 'Greek',
    'nl' => 'Dutch',
);

my %LANGUAGE_CODE_MAP = (
    'eng' => 'en',
    'spa' => 'es',
    'fre' => 'fr',
    'ita' => 'it',
);

# Usage:
# my $locale = Common::Locale->new(countryCode => 'CA', languageCode => 'fr');

sub _init {
    my ( $self, %args ) = @_;
    assert( $args{countryCode},  "ERROR - missing required argument 'countryCode'" );
    assert( $args{languageCode}, "ERROR - missing required argument 'languageCode'" );

    $self->{CountryCode} = $args{countryCode};
    $self->{Country}     = ${Common::Consts::CODE_TO_COUNTRY}{ $self->{CountryCode} };

    $self->{LanguageCode} = $args{languageCode};
    $self->{Language}     = $CODE_TO_LANGUAGE{ $self->{LanguageCode} };

    # Numeric format depends on the language.
    #
    $self->{NumericFormat} = Common::NumericFormat->new( languageCode => $args{languageCode}, countryCode => $args{countryCode} );

    # Numeric format depends on the language.
    #
    $self->{DateTimeFormat} = Common::DateTimeFormat->new( languageCode => $args{languageCode}, countryCode => $args{countryCode} );

    # Currency format depends on the country.
    #
    $self->{CurrencyFormat} = Common::CurrencyFormat->new( countryCode => $args{countryCode} );

    # Secondary market currency format depends on the secondary market country.
    #
    $self->{SecondaryMarketCountryCode} = $args{secondaryMarketCountryCode};
    $self->{SecondaryMarketCurrencyFormat} =
      $self->SecondaryMarketCountryCode ? Common::CurrencyFormat->new( countryCode => $args{secondaryMarketCountryCode} ) : undef;

    # !!! Don't yet know what the DataTimeFormat will depend on.
    #
    return $self;
}

sub countryCode {
    my ($self) = @_;
    return $self->{CountryCode};
}

sub languageCode {
    my ($self) = @_;
    return $self->{languageCode};
}

sub numericFormat {
    my ($self) = @_;
    return $self->{NumericFormat};
}

sub currencyFormat {
    my ($self) = @_;
    return $self->{CurrencyFormat};
}

sub dateTimeFormat {
    my ($self) = @_;
    return $self->{DateTimeFormat};
}

# !!! These will be pass-through to start with...
#
sub formatDate {
    my ( $self, $date ) = @_;

    return '' unless $date;

    return $self->dateTimeFormat()->formatDate($date);
}

sub formatTime {
    my ( $self, $time ) = @_;

    return $self->dateTimeFormat()->formatTime($time);
}

sub formatDateTime {
    my ( $self, $dateTime ) = @_;

    return $self->dateTimeFormat()->formatDateTime($dateTime);
}

# This method formats a 'normal' decimal value into
# the specific format encapsulated by this class.
#
sub formatNumber {
    my ( $self, $number, $precision ) = @_;

    # If this is _not_ a normal decimal value, then
    # just return whatever garbage was passed in.
    #
    if ( !$number =~ /^-?(?:\d+(?:\.\d*)|\.\d+)$/ ) {
        return $number;
    }

    if ( defined $precision ) {

        # We'll use sprintf to 0-pad, but we'll use our internal
        # round method to do the rounding.
        # Otherwise we get 'scientific' rounding, which is not the same
        # as 'accounting' rounding.
        #
        my $format = "%" . $precision . "f";

        my $roundTo = $precision;
        $roundTo =~ s/^(\d+)?\.(\d+)$/$2/g;

        $number = sprintf( $format, Common::RSMath::round( $number, $roundTo ) );

        #        $number = Common::RSMath::round($number, $precision);
        #        $number = sprintf('%'.$precision.'f', $number);
    }
    return $self->numericFormat()->format($number);
}

# Need locale specific validation
#
sub isValidNumber {
    my ( $self, $number ) = @_;

    #  Test for sillyness first.
    #
    return 0 if $number eq '';
    return 0 unless ( $number =~ /.*\d/ );

    my $decimal = $self->numericFormat()->decimal;
    $decimal = '\.' if ( '.' eq $decimal );
    my $thousands = $self->numericFormat()->thousands;
    $thousands = '\.' if ( '.' eq $thousands );

    # Lose the minus sign.
    #
    $number =~ s/^-//;

    # If it has any characters other than a number, or the two separators, it's bogus.
    #
    my $regex = '[^\d|' . $decimal . '|' . $thousands . ']';
    if ( $number =~ m/$regex/ ) {
        return 0;
    }

    # If it has more than 1 'decimal point', it's bogus.
    #
    my $left;
    if ( $number =~ /^(.*)$decimal(.*)/ ) {
        $left = $1;
        my $right = $2;

        if ( !defined $right || length($right) < 1 ) {

            # There was nothing after the decimal point.
            #
            return 0;
        }

        # Shouldn't be any decimal separators on the 'left' side.
        if ( $left =~ /$decimal/ ) {
            return 0;
        }

        # Shouldn't be any thousands separators after the decimal point.
        #
        if ( $right =~ /$thousands/ ) {
            return 0;
        }
    } else {

        # No decimal point.
        #
        $left = $number;
    }

    # Look for a 'trailing' thousands separator,
    # like '123,'
    #
    if ( $left =~ /(.*)$thousands$/ ) {
        return 0;
    }

    # If there are any thousands separators, they need to be properly spaced.
    #
    if ( $left =~ /$thousands/ ) {
        my @chunks = split( /$thousands/, $left );
        if ( scalar @chunks ) {
            while ( defined( my $chunk = pop(@chunks) ) ) {
                my $chunkSize = length($chunk);
                if ( scalar @chunks ) {
                    if ( $chunkSize != 3 ) {
                        return 0;
                    }
                } else {
                    if ( $chunkSize < 1 || $chunkSize > 3 ) {
                        return 0;
                    }
                }
            }
        }
    }

    return 1;
}

sub formatMoney {
    my ( $self, $money, $currencyCode ) = @_;

    my $number = $self->formatNumber( $money, '0.2' );

    # If the optional $currencyCode is passed in, use that rather than
    # our regular currency model.
    #
    my $currencyFormat = $self->currencyFormat();
    if ( defined $currencyCode ) {
        $currencyFormat = Common::CurrencyFormat->new( currencyCode => $currencyCode );
    }
    return $currencyFormat->symbol() . $number;
}

sub formatMoneyWithCurrencyCode {
    my ( $self, $money, $currencyCode ) = @_;

    my $number = $self->formatNumber( $money, '0.2' );

    my $currencyFormat = $self->currencyFormat();
    if ( defined $currencyCode ) {
        $currencyFormat = Common::CurrencyFormat->new( currencyCode => $currencyCode );
    }
    return $currencyFormat->symbol() . $number . $currencyFormat->currencyCode();
}

# This static method will 'scrub' an incoming numeric or monetary value, and return
# the equivalent plain ole' decimal value.
#
sub scrubNumber {
    my ( $self, $inValue ) = @_;

    # Get rid of monetary symbols, and extra characters
    #
    my $decimal = $self->numericFormat()->decimal;
    $decimal = '\.' if ( '.' eq $decimal );
    my $thousands = $self->numericFormat()->thousands;
    $thousands = '\.' if ( '.' eq $thousands );

    my $regex = '[^\d|\-|' . $decimal . '|' . $thousands . ']';
    $inValue =~ s/$regex//g;

    # Split out the fractional part
    #
    my ( $leftSide, $rightSide ) = split( $decimal, $inValue );

    # Now strip away the remaining separators
    #
    $leftSide =~ s/[^\d|\-]//g;

    my $newValue = $leftSide;
    if ($rightSide) {
        $newValue .= '.' . $rightSide;
    }

    return $newValue;
}

sub ThreeLetterToTwoLetterLanguageCode {
    my ($inCode) = @_;

    return $LANGUAGE_CODE_MAP{$inCode};
}

1;
