#------------------------------------------------------------
# Copyright (C) 2008 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------
package Common::NumericFormat;

use lib '/app/tools/common/lib';
use Common::Assert;
use Common::XMLObject;

use base 'Common::XMLObject';

# We'll have some flags we can use to 'tune' output.
# !!! For now, there is just 1 flag:  Whether to
# !!! show the 'thousands' separator or not.
#
# This hash defines both what flags are available, and what their default
# settings are.
#
my %gFlags = ( showThousandsFlag => 1, );

# Keyed by ISO 639-1 language code
#
my %gDefs = (
    'elGR' => { decimal => ',', thousands => '.', },

    'enAU' => { decimal => '.', thousands => ',', },
    'enBG' => { decimal => ',', thousands => ' ', },
    'enBR' => { decimal => ',', thousands => '.', },
    'enCA' => { decimal => '.', thousands => ',', },
    'enDK' => { decimal => ',', thousands => ' ', },
    'enGB' => { decimal => '.', thousands => ',', },
    'enEU' => { decimal => '.', thousands => ',', },
    'enHK' => { decimal => '.', thousands => ',', },
    'enJM' => { decimal => '.', thousands => ',', },
    'enKR' => { decimal => '.', thousands => ',', },
    'enNO' => { decimal => ',', thousands => '.', },
    'enNZ' => { decimal => '.', thousands => ',', },
    'enSE' => { decimal => ',', thousands => ' ', },
    'enUS' => { decimal => '.', thousands => ',', },
    'enZA' => { decimal => '.', thousands => ' ', },

    'esES' => { decimal => ',', thousands => '.', },
    'esMX' => { decimal => '.', thousands => ',', },

    'frFR' => { decimal => ',', thousands => ' ', },
    'frCH' => { decimal => '.', thousands => '\'', },

    'grDE' => { decimal => ',', thousands => '.', },
    'grCH' => { decimal => '.', thousands => '\'', },

    'itIT' => { decimal => ',', thousands => '.', },
    'itCH' => { decimal => '.', thousands => '\'', },

    'isIS' => { decimal => ',', thousands => '.', },

    'jpJP' => { decimal => '.', thousands => ',', },

    'nlNL' => { decimal => ',', thousands => '.', },

    'zhCN' => { decimal => '.', thousands => ',', },

);

sub _init {
    my ( $self, %args ) = @_;

    assert( $args{languageCode}, "ERROR - you must specify a languageCode" );
    assert( $args{countryCode},  "ERROR - you must specify a countryCode" );

    my $languageCode = $args{languageCode};
    my $countryCode  = $args{countryCode};
    my $pair         = "$languageCode$countryCode";
    my $def          = $gDefs{$pair} || die "ERROR - unknown combo '$pair'";

    $self->{Decimal}   = $def->{decimal};
    $self->{Thousands} = $def->{thousands};

    return $self;
}

# This method is used to _temporarily_ override the default formatting settings.
# It returns an object reference - As long as this object remains in scope,
# your new settings will remain in effect.  As soon as it goes _out_ of scope,
# the original settings will revert.
#
# You can in theory nest calls to this method if you want to.
#
sub TemporarySettings {
    my (%flags) = @_;

    my $reverter = Common::NumericFormat::SettingsReverter->new( flags => \%gFlags );

    # !!! Sanity-check the flags args.
    #
    foreach my $inFlag ( keys %flags ) {
        assert( defined $gFlags{$inFlag}, "ERROR - unknown flag $inFlag" );
        $gFlags{$inFlag} = $flags{$inFlag};
    }

    return $reverter;
}

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

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

sub format {
    my ( $self, $number ) = @_;

    # !!! Might be a good idea to have a sanity-check regex here to make
    # sure we're just getting normal decimal values.
    #

    # split out the decimal, if there is one.
    #
    my ( $leftSide, $rightSide ) = split( /\./, $number );

    # We'll make note of whether this is a negative number, then process the
    # absolute value.  That way I don't have to think about sign anymore...
    #
    my $isNegative;
    if ( $leftSide < 0 ) {
        $isNegative = 1;
        $leftSide *= -1;
    }

    # Need to break up the left-side into a character array, so we can insert the
    # correct seperator
    #

    my $outputString;

    if ($isNegative) {
        $outputString .= '-';
    }

    if ( $gFlags{showThousandsFlag} ) {
        my @leftChars  = split( //, $leftSide );
        my $leftString = '';
        my $count      = 0;
        while ( scalar @leftChars ) {
            $count++;
            $leftString = pop(@leftChars) . $leftString;
            if ( 3 == $count && scalar @leftChars ) {
                $leftString = $self->thousands() . $leftString;
                $count      = 0;
            }
        }
        $outputString .= $leftString;
    } else {
        $outputString .= $leftSide;
    }

    if ( defined $rightSide ) {
        $outputString .= $self->decimal();
        $outputString .= $rightSide;
    }

    return $outputString;
}

#
# --------------------------------------------------------------------------------------------
#

package Common::NumericFormat::SettingsReverter;

use lib '/app/tools/common/lib';
use Common::Assert;
use Common::XMLObject;

sub new {
    my ( $class, %args ) = @_;

    my $self = bless {}, $class;

    return $self->_init(%args);
}

sub _init {
    my ( $self, %args ) = @_;

    my $refToFlags = $args{flags};
    assert( defined $refToFlags, "ERROR - missing flags argument" );

    # If this was C++, this class would be a private memeber class.
    # Hence, it's ok that I'm going to violate encapsulation here...
    #
    $self->{_refToFlags} = $refToFlags;
    foreach my $flag ( keys %{$refToFlags} ) {
        $self->{_oldFlags}{$flag} = $refToFlags->{$flag};
    }

    return $self;
}

sub DESTROY {
    my $self = shift;

    foreach my $flag ( keys %{ $self->{_oldFlags} } ) {
        $self->{_refToFlags}{$flag} = $self->{_oldFlags}{$flag};
    }
}

1;
