#------------------------------------------------------------
# Copyright (C) 2010 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------
package BookPub::Catalog::Chart::BookPrice;

use strict;
use warnings;

use Data::Dumper;
use File::Temp;
use File::Basename;
use Data::Dumper;
use Date::Calc;

use Date::Manip;

use lib '/usr/local/chart_director/lib';
use perlchartdir;

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

use lib '/app/tools/bookpub/lib';
use BookPub::Analytics::ProductMonitor::Data::MonitoredPrices;
use BookPub::DB::Item::BookProduct;
use BookPub::Catalog::Chart::Base;
use BookPub::Catalog::GroupedBookProducts;
use BookPub::DB::Item::Region;
use BookPub::DB::Item::ProductMarket;
use BookPub::DB::Item::ProductMarketPrice;

use base 'BookPub::Catalog::Chart::Base';

use constant kLineColor => 0x989898;

sub _generateChartImage {
    my $self = shift;

    my $currencySymbol = Common::Client::Current()->Locale()->currencyFormat()->symbol();
    my $thousands      = Common::Client::Current()->Locale()->numericFormat()->thousands();
    my $decimal        = Common::Client::Current()->Locale()->numericFormat()->decimal();

    my $c = new XYChart( 640, 250 );

    # Set the plotarea at (30, 20) and of size 400 x 200 pixels
    $c->setPlotArea( 85, 20, 490, 200 );

    my $layer = $c->addLineLayer2();

    my $chartData = $self->_getBookChartData($layer);

    $layer->setLineWidth(2);

    # Force the y-axis to start at zero
    $c->yAxis->setLinearScale( 0, $perlchartdir::NoValue );
    $c->setClipping();

    # Set the colors
    $c->setColor( $perlchartdir::TextColor, BookPub::Catalog::Chart::Base::kTextColor );

    # Set the labels on the x axis.
    $c->xAxis()->setLabels( $chartData->{labels} );

    # Set the label step (allows us to limit number of labels displayed).
    $c->xAxis()->setLabelStep( $self->{_labelStep} );

    # Set the label gap (spacing between the label and the chart).
    $c->xAxis()->setLabelGap(BookPub::Catalog::Chart::Base::kLabelGap);

    # Grey out the area without data.
    $c->xAxis()->addZone( 0, $chartData->{firstNonEmptyElement}, BookPub::Catalog::Chart::Base::kNoDataColor );

    # Set the y axis label formatting.
    $c->yAxis()->setLabelFormat( $currencySymbol . "{value|2" . $thousands . $decimal . "}" );

    # Set the label gap (spacing between the label and the chart).
    $c->yAxis()->setLabelGap(BookPub::Catalog::Chart::Base::kLabelGap);

    my $chartName = $self->_getChartFilename();

    $self->{ChartSource} = sprintf( "%s/%s", BookPub::Catalog::Chart::Base::kChartImgDir, basename($chartName) );

    # Output the chart
    my $result = $c->makeChart($chartName);

    # Create the image map
    $self->setImageMap( imageMap => $c->getHTMLImageMap( '', '', 'title="%%{value|2}%%"' ) );
}

sub _getBookChartData {
    my $self  = shift;
    my $layer = shift;

    my $bookID    = $self->{_bookID};
    my $offset    = $self->{_offset};
    my $dateStart = $self->{_dateStart};
    my $dateEnd   = $self->{_dateEnd};
    my $serviceID = $self->{_serviceID};

    my @productIDs = $self->_getBookProductIDs();

    my $graphData = BookPub::Analytics::ProductMonitor::Data::MonitoredPrices->new(
        productID => \@productIDs,
        serviceID => $serviceID,
        dateStart => $dateStart,
        dateEnd   => $dateEnd
    );

    my @legend;
    my @hardbackColors = (
        0x5aaa5a,    # green
        0x996633,    # light brown
        0xCC99CC,    # lavender
        0x669999,    # teal
        0x14f9b0,    # aquamarine
        0xf13357,    # red
        0x0530e7,    # royal blue
        0x72f346,    # light green
    );
    my @paperbackColors = (
        0x15b6f9,    # sky blue
        0xf34697,    # pink
        0x336699,    # blue
        0xd9b25e,    # tan
        0xc4d505,    # dark yellow
        0x86a880,    # sea foam green
        0x333366,    # dark blue
        0x8615f9,    # royal purple
    );
    my @ebookColors = (
        0xefac2c,    # orange
        0xf4ef64,    # yellow
        0xf97b15,    # burnt orange
        0x999999,    # grey
        0x594330,    # green olive brown
        0xbe33f1,    # purple
        0xa0bdc4,    # light blue
        0x663333,    # brown/red/purple
    );

    my $firstNonEmptyElement;

    my $hardbackCount  = 0;
    my $paperbackCount = 0;
    my $ebookCount     = 0;

    foreach (@productIDs) {
        my $monitoredPrices = $graphData->getServiceDataForProductID( $serviceID, $_ );

        # By default, we'll assume there is no data, and then change this value if we find something
        if ( !defined($firstNonEmptyElement) ) {
            $firstNonEmptyElement = @{$monitoredPrices};
        }

        if ( $firstNonEmptyElement != 0 ) {
            my $j = 0;
            foreach ( @{$monitoredPrices} ) {
                if ( !$_ || $_ eq '' ) {

                    # No data here
                    $j++;
                    next;
                }
                if ( $j < $firstNonEmptyElement ) {

                    # Found data, but only change the value if it's less than it's previous setting
                    $firstNonEmptyElement = $j;
                    last;
                }
            }
        }

        my $hasData = 0;

        foreach ( @{$monitoredPrices} ) {
            if ( $_ && $_ ne '' ) {
                $hasData = 1;
                last;
            }
        }

        # We only want to include them if there's data.
        #
        if ( $hasData == 1 ) {

            # Need to substitue $perlchartdir::NoValue for empty values.
            # Otherwise, chart director treats tham like zeroes.
            #
            $_ ||= $perlchartdir::NoValue for @{$monitoredPrices};

            my $lineColor;

            if ( BookPub::DB::Item::BookProduct->IsEBook( productID => $_ ) ) {
                $lineColor = $ebookColors[$ebookCount];
                $ebookCount++;
            } elsif ( BookPub::DB::Item::BookProduct->IsHardback( productID => $_ ) ) {
                $lineColor = $hardbackColors[$hardbackCount];
                $hardbackCount++;
            } elsif ( BookPub::DB::Item::BookProduct->IsPaperback( productID => $_ ) ) {
                $lineColor = $paperbackColors[$paperbackCount];
                $paperbackCount++;
            }

            $layer->addDataSet( $monitoredPrices, $lineColor, "Monitored Price" );

            my $legendItem = {};

            # Now let's add the product ID and the line color to the XML,
            # so that we can create a legend.
            $legendItem->{ProductID} = $_;
            $legendItem->{Color} = sprintf( "#%x", $lineColor );

            push( @legend, $legendItem );

        }

    }

    $self->{Legend}{Item} = \@legend;

    # Grab the dates to use as labels
    #
    my $labels = $graphData->getDates();

    return { labels => $labels, firstNonEmptyElement => $firstNonEmptyElement };
}

sub _getBookProductIDs {
    my $self = shift;

    my $bookID = $self->{_bookID};

    my $masterBookProductList = BookPub::Catalog::BookProductList->new( bookID => $bookID );
    my $sortedBookProductList = BookPub::Catalog::GroupedBookProducts->new( bookProductList => $masterBookProductList );

    my @productIDs;
    my %similarProductIDs;

    my $sortedBookListArrayRef = $sortedBookProductList->{BookProductList};
    foreach my $bookList ( @{$sortedBookListArrayRef} ) {
        my $sortedBookProductArrayRef = $bookList->{BookProduct};
        foreach my $bookProduct ( @{$sortedBookProductArrayRef} ) {
            my $productID = $bookProduct->{ProductID};
            if ( BookPub::DB::Item::BookProduct->IsEBook( productID => $productID ) ) {

                # Check if we've already seen this product id
                if ( !$similarProductIDs{$productID} ) {

                    # If we haven't, add it to the list and then mark it
                    # and it's similar products so that we don't include them again.
                    #
                    push( @productIDs, $productID );
                    my $similarProductList = $self->_getSimilarProducts($productID);
                    foreach ( @{$similarProductList} ) {
                        $similarProductIDs{$_} = 1;
                    }
                }
            } else {
                push( @productIDs, $productID );
            }
        }
    }

    return @productIDs;
}

sub _getSimilarProducts {
    my ( $self, $productID ) = @_;
    assert($productID);

    # By default we'll use the most restrictive notion of similarity.
    # For eBooks, that means products that share the same expected price.
    # Now, price exists at a moment in time... I'll try the end date of our date range.
    #
    my $productMarket = $self->_productMarket($productID);
    my $marketID      = $productMarket->market_id();
    my $currentPrice  = BookPub::DB::Item::ProductMarketPrice->GetCurrentPrice(
        product_market_id => $productMarket->product_market_id(),
        date              => $self->{_dateEnd},
        currency_code     => $self->_currencyCode(),
    );

    my $price = $currentPrice->price();

    my @productIDArray = BookPub::DB::Item::BookProduct->GetProductIDsOfSimilarProducts(
        $productID,
        marketID     => $marketID,
        currencyCode => $self->_currencyCode(),
        price        => $price,
    );

    return \@productIDArray;
}

sub _productMarket {
    my ( $self, $productID ) = @_;
    assert($productID);

    if ( !$self->{_productMarket}{$productID} ) {
        my $usRegions = BookPub::DB::Item::Region->GetAllRegions( country_code => $self->_countryCode() );
        die "ERROR - unable to find a region for " . $self->_countryCode() unless $usRegions->size() > 0;

        my @regionIDs;
        while ( my $region = $usRegions->next() ) {
            push @regionIDs, $region->region_id;
        }

        my $productMarkets = BookPub::DB::Item::ProductMarket->GetAllByRegion( region_id => \@regionIDs, product_id => $productID );

        # !!! I am going to assume just 1.   This might be false...
        #
        die "ERROR - More than one product_market associated with product: $productID, country " . $self->_countryCode()
          if ( $productMarkets->size() > 1 );

        die "ERROR - No product_market found for product: $productID, country " . $self->_countryCode()
          if ( $productMarkets->size() == 0 );

        my $productMarket = $productMarkets->next();
        $self->{_productMarket}{$productID} = $productMarket;
    }

    return $self->{_productMarket}{$productID};
}

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

    # !!! At the moment we hard-code this.  Perhaps some day this will become a parameter.
    #
    return 'US';
}

sub _currencyCode {
    my ($self) = @_;
    return 'USD';
}

###
1;    #
###
