#---------------------------------------------------------------
# ____                   _ _         ____  _
#|  _ \ ___  _   _  __ _| | |_ _   _/ ___|| |__   __ _ _ __ ___
#| |_) / _ \| | | |/ _` | | __| | | \___ \| '_ \ / _` | '__/ _ \
#|  _ < (_) | |_| | (_| | | |_| |_| |___) | | | | (_| | | |  __/
#|_| \_\___/ \__, |\__,_|_|\__|\__, |____/|_| |_|\__,_|_|  \___|
#            |___/             |___/
#
# Copyright (C) 2009 RoyaltyShare, Inc.   All Rights Reserved
# $Id$
#---------------------------------------------------------------
package BookPub::Sale::Report::Royalty::Configurable;
use strict;
use warnings;

use IO::File;
use File::Path;

use lib '/app/tools/common/lib';
use lib '/app/tools/bookpub/lib';
use BookPub::DB::Item::ReportConfig;
use BookPub::DB::Item::Service;
use BookPub::Config;
use Common::Util;
use Common::Assert;

use base 'BookPub::Sale::Report::Royalty';

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

    #    assert(defined $args{periodID});

    $self->{_reportConfigID} = $args{reportConfigID};

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

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

    return 'BookPub::Sale::Report::Royalty::Configurable::Row';
}

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

    if ( !$self->{_reportConfig} ) {
        $self->{_reportConfig} = BookPub::DB::Item::ReportConfig->Lookup( report_config_id => $self->{_reportConfigID} );
    }

    return $self->{_reportConfig};
}

# !!! Should override this to use a clean version of the config name
#
sub fileName {
    my ($self) = @_;

    my $suffix = '.txt';

    my $config = $self->_getReportConfig;
    $suffix = '.tsv' if 'tab' eq $config->delimiter();
    $suffix = '.csv' if 'comma' eq $config->delimiter();
    return $self->{_periodID} . '_RoyaltyReport' . $suffix;
}

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

    # We're going to be building a query based on the configuration
    #
    my $config  = $self->_getReportConfig();
    my $columns = $config->columns();
    return BookPub::DB::Item::SaleReport->ReportQuery( $columns, $self->periodID() );
}

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

    # Make sure we have a directory to write the final file into.
    #
    my $baseDir = $self->_baseDir();
    if ( !-d $baseDir ) {
        File::Path::mkpath($baseDir) || die "ERROR - make_path failed for $baseDir: $!";
    }

    my $dest_file_path = $self->filePath();

    my $writeFH;
    unless ( $writeFH = IO::File->new(">$dest_file_path") ) {
        die "can't create file $dest_file_path: $!\n";
    }
    binmode( $writeFH, ':utf8' );

    # Output the header
    #
    my $header = $self->getHeader();
    print $writeFH "$header\n";

    while ( my $row = $self->report->getNextRow() ) {
        print $writeFH "$row\n";
    }

    close($writeFH);

    return 1;

}

sub createExcel {
}

# !!! Experimental interface - Hooking this up with RSApache::Response::ReportDownload
#
# Sadly, just not going to work.  I really cannot construct a query that can deal correctly with books and chapters.
#

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

    if ( !$self->{header} ) {
        my $config = $self->_getReportConfig();
        my $delim  = $config->delimiter();
        my $delimChar;
        $delimChar = "\t" if ( 'tab' eq $delim );
        $delimChar = ","  if ( 'comma' eq $delim );
        $delimChar = "|"  if ( 'pipe' eq $delim );

        my $columns = $config->columns();
        $self->{header} = join( $delimChar, @$columns );
    }
    return $self->{header};
}

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

    if ( !$self->{streamCollection} ) {
        $self->{streamCollection} = $self->_getSalesCollection();
    }
    return undef unless $self->{streamCollection}->hasNext();

    my $sale     = $self->{streamCollection}->next();
    my $rowClass = $self->getRowClass();

    # All aggregation has been done already.
    # Actually... we ONLY aggregate by product_id (which is 1-1 with 'format') and territory.
    # Everything else is just data.
    #
    my $row = $rowClass->new( sale => $sale, config => $self->_getReportConfig() );
    return $row->text();
}

###
1;    #
###

package BookPub::Sale::Report::Royalty::Configurable::Row;
use strict;
use warnings;

use Data::Dumper;

use lib '/app/tools/common/lib';
use lib '/app/tools/bookpub/lib';
use BookPub::DB::Item::ReportConfig;
use BookPub::DB::Item::SaleReport;
use BookPub::DB::Item::ChapterProduct;
use BookPub::DB::Item::BookProduct;
use BookPub::DB::Item::Book;

#use BookPub::DB::Item::BookFormat;
use BookPub::DB::Item::Chapter;
use BookPub::DB::Item::BookContributor;
use BookPub::DB::Item::ChapterContributor;
use BookPub::DB::Item::Contributor;
use BookPub::DB::Item::Service;

use BookPub::DB::Item::Service;
use BookPub::DB::Item::File;
use BookPub::Config;
use BookPub::Catalog::Imprint;
use BookPub::Matcher::Product;
use BookPub::Catalog::ProductFormat;
use Common::Util;
use Common::Assert;
use Common::RSApp;

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

    my $self = bless {}, $class;
    return $self->_init(%args);
}

sub _init {
    my ( $self, %args ) = @_;
    assert( $args{sale} );
    assert( $args{config} );

    # !!! sale is a DB::Item::SaleReport object.
    #

    $self->{_sale}   = $args{sale};
    $self->{_config} = $args{config};

    # Decipher the 'delimiter' into the actual character.
    # !!! Might be nice to provide an interface for this in the ReportConfig class.
    #
    my $delim = $self->{_config}->delimiter();
    $self->{_textDelimiter} = "\t" if ( 'tab' eq $delim );
    $self->{_textDelimiter} = ","  if ( 'comma' eq $delim );
    $self->{_textDelimiter} = "|"  if ( 'pipe' eq $delim );

    # We'll be lazy, and wait to create the output line until the last moment.
    #
    return $self;
}

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

    if ( !$self->{_asText} ) {
        $self->{_asText} = $self->_getText();
    }
    return $self->{_asText};
}

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

    my @columnData;
    my $columns = $self->{_config}->columns();

    my $delim = $self->{_textDelimiter};

    # So, SOME of these things will
    foreach my $colName (@$columns) {
        my $data;

        # If we've created a special accessor method in this 'Row' class, we'll use
        # that.  Otherwise we'll assume the data is present in the 'sale' DB::Item object.
        # NOTE:  We can't turn this logic around, because 'can' will not work with the DB::Item
        # class...  This is because we use AUTOLOAD to create methods.  We could just look at
        # the underlying hash reference, and see if it has a member, but that violates encapsulation.
        #
        if ( $self->can($colName) ) {
            $data = $self->$colName();
        } else {
            $data = $self->{_sale}->$colName();
        }

        # Scrub the delimeter from the data.
        #
        $data =~ s/$delim/ /g;
        push @columnData, $data;
    }

    return join( $self->{_textDelimiter}, @columnData );
}

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

    if ( !$self->{_book} ) {
        my $sale      = $self->{_sale};
        my $productID = $self->{_sale}->product_id();
        assert( $productID, "No product_id?  You're doing it wrong..." );

        # If this is a chapter_product, then we want to fetch the associated book.
        # If it's a book product, fetch the book.
        my $product = BookPub::DB::Item::ChapterProduct->Lookup( product_id => $productID );
        if ( !$product ) {
            $product = BookPub::DB::Item::BookProduct->Lookup( product_id => $productID );
        }

        $self->{_book} = BookPub::DB::Item::Book->Lookup( book_id => $product->book_id );
    }
    return $self->{_book};
}

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

    if ( !$self->{_chapterLookedUp} ) {
        $self->{_chapterLookedUp} = 1;

        my $sale      = $self->{_sale};
        my $productID = $self->{_sale}->product_id();
        assert( $productID, "No product_id?  You're doing it wrong..." );

        # If this is a chapter_product, then we want to fetch the associated book.
        # If it's a book product, fetch the book.
        my $product = BookPub::DB::Item::ChapterProduct->Lookup( product_id => $productID );
        if ($product) {
            $self->{_chapter} = BookPub::DB::Item::Chapter->Lookup( chapter_id => $product->chapter_id );
        }
    }

    return $self->{_chapter};
}

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

    my $book = $self->_getAssociatedBook();
    return undef unless $book;
    return $book->title();
}

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

    my $book = $self->_getAssociatedBook();
    return undef unless $book;
    return $book->subtitle();
}

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

    my $chapter = $self->_getAssociatedChapter();
    return undef unless $chapter;
    return $chapter->title();
}

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

    my $chapter = $self->_getAssociatedChapter();
    return undef unless $chapter;
    return $chapter->title();
}

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

    my $serviceID = $self->{_sale}->rs_service_id();
    assert( $serviceID, "No service_id?  You're doing it wrong..." );

    my $service = BookPub::DB::Item::Service->Lookup( service_id => $serviceID );
    return undef unless $service;
    return $service->service_name;
}

# !!! This is somewhat misleading.
# !!! We want to serve up the client's name/id for the service, not our internal id.
#
sub service_id {
    my ($self) = @_;

    my $serviceID = $self->{_sale}->rs_service_id();
    assert( $serviceID, "No service_id?  You're doing it wrong..." );

    my $service = BookPub::DB::Item::Service->Lookup( service_id => $serviceID );
    return undef unless $service;
    return $service->client_service_name;
}

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

    # !!! Fun fact - We haven't figured out what the 'chapter_id' is yet!
    #
    return undef;
}

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

    my $sale      = $self->{_sale};
    my $productID = $self->{_sale}->product_id();
    assert( $productID, "No product_id?  You're doing it wrong..." );

    # If this is a chapter_product, then we want to fetch the associated book.
    # If it's a book product, fetch the book.
    my $product = BookPub::DB::Item::ChapterProduct->Lookup( product_id => $productID );
    if ( !$product ) {
        $product = BookPub::DB::Item::BookProduct->Lookup( product_id => $productID );
    }

    # Look up the onix codes associated with this format;
    #
    my $description;
    my $productFormat = BookPub::Catalog::ProductFormat->new( bookFormatID => $product->book_format_id );

    if ($productFormat) {
        $description = $productFormat->asString();
    }

    # SHOULD this be 'description'?  Or perhaps the ONIX code?
    #
    return $description;
}

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

    my $book = $self->_getAssociatedBook();
    return undef unless $book;

    my @authorNames = BookPub::DB::Item::BookContributor->GetAuthorLastNamesByBookID( $book->book_id );

    # For now, we will just use a space as the delimiter.
    # We may want to make this smart, and use ',' if it's NOT a comma-seperated report.
    #
    my $authorNameString = join( ' ', @authorNames );
    return $authorNameString;
}

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

    my $productID = $self->{_sale}->product_id();
    assert( $productID, "No product_id?  You're doing it wrong..." );

    # If this is actually a book_product, we'll have an isbn10.
    #
    my $bookProduct = BookPub::DB::Item::BookProduct->Lookup( product_id => $productID );
    return undef unless $bookProduct;
    return $bookProduct->isbn10;
}

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

    my $productID = $self->{_sale}->product_id();
    assert( $productID, "No product_id?  You're doing it wrong..." );

    # If this is actually a book_product, we'll have an isbn13.
    #
    my $bookProduct = BookPub::DB::Item::BookProduct->Lookup( product_id => $productID );
    return undef unless $bookProduct;
    return $bookProduct->isbn13;
}

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

    my $book = $self->_getAssociatedBook();
    return undef unless $book;

    my $imprint = BookPub::DB::Item::Imprint->Lookup( imprint_id => $book->imprint_id );
    return $imprint->name();
}

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

    return undef;
}

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

    return undef;
}

###
1;    #
###
