#---------------------------------------------------------------
# ____                   _ _         ____  _
#|  _ \ ___  _   _  __ _| | |_ _   _/ ___|| |__   __ _ _ __ ___
#| |_) / _ \| | | |/ _` | | __| | | \___ \| '_ \ / _` | '__/ _ \
#|  _ < (_) | |_| | (_| | | |_| |_| |___) | | | | (_| | | |  __/
#|_| \_\___/ \__, |\__,_|_|\__|\__, |____/|_| |_|\__,_|_|  \___|
#            |___/             |___/
#
# Copyright (C) 2011 RoyaltyShare, Inc.   All Rights Reserved
#---------------------------------------------------------------

package Report::Dynamic::SingleQuery;
use strict;

use lib '/app/tools/common/lib';
use lib '/app/tools/report/lib';
use lib '/app/tools/appuser/lib';
use base 'Report::Dynamic';

use Common::UTF8;
use Common::Log;
use Common::Assert;
use File::Path;
use Date::Calc;
use AppUser::DB::Item::User;
use Common::DB::Item::Country;
use Common::Client;

#
# For the majority of subclasses, you will just need to override the next two methods (and one
# just returns a config hash, no logic ).
#

sub _columns {
    my ($self) = @_;

    # Override this to return an array reference of column definitions.
    # What we want to do here is specify the pretty text that should appear
    # in the header, and the field name from the DB::Item for each row.
    # Note that we should allow either of these to be blank - We might
    # want to have an empty column, or dynamically generate the Header, or
    # whatever.   So, in general, this array should look something like this:
    #
    # return
    # [
    #    {
    #       header =>
    #       {
    #          name => 'Run ID',
    #       },
    #       column =>
    #       {
    #          field => 'artist_royalty_run_id',
    #       },
    #    },
    #    {
    #       header =>
    #       {
    #          name => 'Status',
    #       },
    #       column =>
    #       {
    #          field => 'status',
    #       },
    # ... more column definitions
    # ];
    #
    # By having lots of hashes, it leaves room to grow.  For example, we may want to specify
    # formatting options to be passed along to Excel file generation process.  Or we might want
    # to specify additional processing directive (maybe even include function references) to manipulate
    # the data further.
    # For now I'm not going to implement any of that - We'll see what needs arise first.

    assert( 0, 'override' );
}

sub _getCollection {
    my ($self) = @_;

    # Override this to return a DB::ItemCollection of row data.
    # Essentially, you will usually be making a call into a DB::Item
    # class, passing along the correct parameters.
    # That's going to be left as an exercise for the reader...
    #
    # but will probably be something like:
    #  return RPS::DB::Item::ReportQuery::ArtistPayee->GetReport(
    #   some_parameter => $self->parameters()->SomeParameter(),
    #   another_param  => $self->parameters()->AnotherParam(),
    #  );
    #
    #

    assert( 0, 'override' );
}

# !!! I'm thinking we can use the _columns array to build this out.
# !!! Probably the easiest scheme would just to add a 'excel' field to 'column' that contains the
# !!! formatting template (i.e. 'text', 'number', 'number:0.00', etc.)
# !!! If there isn't one, we'll ignore that column.
#
sub _getExcelTemplate {
    my ($self) = @_;

    my @templates;
    my $columns = $self->_columns();
    for ( my $i = 0 ; $i < scalar @$columns ; $i++ ) {
        my $columnDef = $columns->[$i];
        if (   $columnDef->{column}
            && $columnDef->{column}{excel} ) {
            my $colName     = _indexToColumn($i);
            my $excelFormat = $columnDef->{column}{excel};

            # The date format is dependant on the client's settings. I.e. it will
            # have been localized already.  We need to tell csv2excel how to parse these
            # dates so it can convert them correctly.
            #
            if ( $excelFormat =~ /date/ ) {

                # !!! I suppose we _might_ want to force a certain parsing structure. In other words,
                # !!! we should probably allow something like 'date:Y/M/D' if we know that the dates
                # !!! are NOT localized for some reason or another.
                #
                if ( $excelFormat !~ /date:/ ) {
                    $excelFormat .= ':' . Common::Client::Current()->Locale()->dateTimeFormat()->dateFormat();
                }
            }
            push @templates, $colName . ':' . $excelFormat;
        }
    }

    return undef unless scalar @templates;
    return join( ',', @templates );
}

sub _indexToColumn {
    my ($index) = @_;

    # 0 == 'A', 1 == 'B', etc.
    #
    my $bigLetter   = int( $index / 26 );
    my $smallLetter = int( $index % 26 );

    my $result;
    if ($bigLetter) {
        $bigLetter--;
        $result .= chr( 65 + $bigLetter );
    }
    $result .= chr( 65 + $smallLetter );
    return $result;
}

# We override the _header method to return the Header text based on the _columns array.
# This means that classes that inherit from this class won't have to mess with _header.
#
sub _header {
    my ($self) = @_;

    my @headerText;
    my $columnData = $self->_columns();

    foreach my $colItem (@$columnData) {

        # Just the text.
        #
        my $text = '';
        if ( defined $colItem->{header} && defined $colItem->{header}{name} ) {
            $text = $colItem->{header}{name};
        }
        if ( defined $colItem->{header} && defined $colItem->{header}{method} ) {
            my $method = $colItem->{header}{method};
            $text = $self->$method($text);
        }

        push @headerText, $text;
    }
    return \@headerText;
}

sub _nextReportLineData {
    my ($self) = @_;

    if ( !$self->{_singleQueryCollection} ) {
        $self->{_singleQueryCollection} = $self->_getCollection();
    }

    my $nextItem = $self->{_singleQueryCollection}->next();
    return undef unless $nextItem;

    my $lineData = $self->_lineDataItemToArray($nextItem);
    return $lineData;

}

sub _lineDataItemToArray {
    my ( $self, $item ) = @_;

    my @outArray;
    my $columnConfig = $self->_columns();
    foreach my $col (@$columnConfig) {
        my $value;
        if ( $col->{column}{method} ) {
            my $method = $col->{column}{method};
            my $field  = $col->{column}{field};
            $value = $self->$method( row => $item, field => $field );
        } elsif ( $col->{column}{field} ) {
            my $field = $col->{column}{field};
            $value = $item->$field();
        } else {
            $value = $col->{column}{value};
        }

        # !!! I'm getting some 'wide character' complaints out of Text::CSV::Encoded.
        #        $value = Common::UTF8::Encode($value);
        push @outArray, $value;
    }

    return \@outArray;
}

# Some helpful filter methods (i.e. things you can use as a 'method' argument in your Column definition).
#
sub _userIDToUserName {
    my ( $self, %args ) = @_;
    my $rowItem   = $args{row};
    my $fieldName = $args{field};

    my $userName;
    my $userID = $rowItem->$fieldName();
    if ($userID) {
        my $userItem = AppUser::DB::Item::User->Lookup( user_id => $userID );
        if ($userItem) {
            $userName = $userItem->first_name . " " . $userItem->last_name;
        }
    }

    return $userName;
}

sub _countryCodeToCountryName {
    my ( $self, %args ) = @_;
    my $rowItem   = $args{row};
    my $fieldName = $args{field};

    my $countryCode = $rowItem->$fieldName();
    my $countryName = $countryCode;
    if ( $countryCode && $countryCode ne 'All' ) {
        my $countryItem = Common::DB::Item::Country->Lookup( alpha2 => $countryCode );
        if ($countryItem) {
            $countryName = $countryItem->name;
        }
    }

    return $countryName;
}

sub _formatDate {
    my ( $self, %args ) = @_;
    my $rowItem   = $args{row};
    my $fieldName = $args{field};

    my $prettyDate;
    my $dateString = $rowItem->$fieldName();
    if ($dateString) {

        # Don't want to display the default 'null' date - Just leave that as a empty string.
        #
        if ( $dateString !~ /^0000-00-00/ ) {

            #            $prettyDate = Common::Client::Current()->Locale()->formatDate($dateString);
            # Not going to localize anymore.  However, we still want to strip out NULL dates.
            #
            $prettyDate = $dateString;
        }
    }

    return $prettyDate;
}

sub _boolToString {
    my ( $self, %args ) = @_;
    my $rowItem   = $args{row};
    my $fieldName = $args{field};

    my $bool = $rowItem->$fieldName();
    return 'yes' if $bool;
    return 'no';
}

sub _formatMoney {
    my ( $self, %args ) = @_;
    my $rowItem   = $args{row};
    my $fieldName = $args{field};

    my $money = $rowItem->$fieldName();
    if ( defined $money ) {
        return Common::Client::Current()->Locale()->formatNumber( $money, '0.2' );
    }
}

sub _appendCurrencyCode {
    my ( $self, $headerText ) = @_;

    return $headerText . ' (' . lc( Common::Client::Current()->Locale()->currencyFormat()->currencyCode() ) . ')';
}

1;
