#---------------------------------------------------------------
# ____                   _ _         ____  _
#|  _ \ ___  _   _  __ _| | |_ _   _/ ___|| |__   __ _ _ __ ___
#| |_) / _ \| | | |/ _` | | __| | | \___ \| '_ \ / _` | '__/ _ \
#|  _ < (_) | |_| | (_| | | |_| |_| |___) | | | | (_| | | |  __/
#|_| \_\___/ \__, |\__,_|_|\__|\__, |____/|_| |_|\__,_|_|  \___|
#            |___/             |___/
#
# Copyright (C) 2011 RoyaltyShare, Inc.   All Rights Reserved
# $Id$
#---------------------------------------------------------------
package BookPub::Analytics::ProductMonitor::Data::MonitoredPrices;
use strict;

use Date::Calc;

use lib '/app/tools/common/lib';
use lib '/app/tools/bookpub/lib';
use Common::XMLObject;
use Common::Assert;
use Common::Log;
use Common::Client;
use BookPub::DB::Item::PriceType;
use BookPub::DB::Item::ProductMonitorItemPrice::JoinedWithProductMonitorItem;
use BookPub::DB::Item::Region;
use BookPub::DB::Item::ProductMarket;
use BookPub::DB::Item::ProductMarketPrice;

use base 'BookPub::Analytics::ProductMonitor::Data';

# Accessor for the extra data this class encapsulates.
#
sub getExpectedPricesForProductID {
    my ( $self, $productID ) = @_;

    if ( !$self->{expectedPrices}{$productID} ) {
        $self->_loadData();
    }

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

sub _getServiceDataCollection {
    my ( $self, $serviceID, $productIDs ) = @_;

    my $collection = BookPub::DB::Item::ProductMonitorItemPrice::JoinedWithProductMonitorItem->GetAllProductPricesForService(
        service_id => $serviceID,
        product_id => $productIDs
    );
    return $collection;
}

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

    return $item->price();
}

sub _dateFromItem {
    my ( $self, $item ) = @_;
    return $item->date_created();
}

sub _getDataCollections {
    my ( $self, $serviceIDs, $productID ) = @_;

    # Call the inherited method first, to load all the service-id based stuff.
    #
    $self->SUPER::_getDataCollections( $serviceIDs, $productID );

    my $productMarket   = $self->_productMarket($productID);
    my $productMarketID = $productMarket->product_market_id;

    $self->{_expectedPriceData}{$productID}{ BookPub::DB::Item::PriceType::kPriceTypeRRP() } =
      BookPub::DB::Item::ProductMarketPrice->GetAllPrices(
        product_market_id => $productMarketID,
        start_date        => $self->{dateStart},
        end_date          => $self->{dateEnd},
        price_type_id     => BookPub::DB::Item::PriceType::kPriceTypeRRP,
        currency_code     => $self->_currencyCode(),
        oldest_first      => 1,
      );

    $self->{_expectedPriceData}{$productID}{ BookPub::DB::Item::PriceType::kPriceTypeRRPPlusTax() } =
      BookPub::DB::Item::ProductMarketPrice->GetAllPrices(
        product_market_id => $productMarketID,
        start_date        => $self->{dateStart},
        end_date          => $self->{dateEnd},
        price_type_id     => BookPub::DB::Item::PriceType::kPriceTypeRRPPlusTax,
        currency_code     => $self->_currencyCode(),
        oldest_first      => 1,
      );

    $self->{_expectedPriceData}{$productID}{ BookPub::DB::Item::PriceType::kPriceTypeAgency() } =
      BookPub::DB::Item::ProductMarketPrice->GetAllPrices(
        product_market_id => $productMarketID,
        start_date        => $self->{dateStart},
        end_date          => $self->{dateEnd},
        price_type_id     => BookPub::DB::Item::PriceType::kPriceTypeAgency,
        currency_code     => $self->_currencyCode(),
        oldest_first      => 1,
      );

    $self->{_expectedPriceData}{$productID}{ BookPub::DB::Item::PriceType::kPriceTypeAgencyPlusTax() } =
      BookPub::DB::Item::ProductMarketPrice->GetAllPrices(
        product_market_id => $productMarketID,
        start_date        => $self->{dateStart},
        end_date          => $self->{dateEnd},
        price_type_id     => BookPub::DB::Item::PriceType::kPriceTypeAgencyPlusTax,
        currency_code     => $self->_currencyCode(),
        oldest_first      => 1,
      );

    # Prime the iterator variables.
    #
    foreach my $priceTypeID ( keys %{ $self->{_expectedPriceData}{$productID} } ) {
        $self->{_currentExpectedPrice}{$productID}{$priceTypeID} = undef;
        $self->{_nextExpectedPrice}{$productID}{$priceTypeID}    = $self->{_expectedPriceData}{$productID}{$priceTypeID}->next();

    }
}

sub _handleDate {
    my ( $self, $productID, $y, $m, $d, $serviceIDs ) = @_;

    # Call the inherited method first, to deal with the service_id data.
    #
    $self->SUPER::_handleDate( $productID, $y, $m, $d, $serviceIDs );

    # Now handle expected prices.
    #
    my $expectedPrice;
    foreach my $priceTypeID ( keys %{ $self->{_expectedPriceData}{$productID} } ) {
        if ( !defined $self->{_currentExpectedPrice}{$productID}{$priceTypeID} ) {
            while ( defined $self->{_nextExpectedPrice}{$productID}{$priceTypeID} ) {

                # !!! start_date CAN BE NULL.
                # !!! Not sure if that is going to screw up the Delta_Days logic or not.
                # !!! Yeah, it will.
                #
                my ( $priceYear, $priceMonth, $priceDay ) =
                  Common::Util::decodeDateMysql( $self->{_nextExpectedPrice}{$productID}{$priceTypeID}->start_date() );
                if ( !$priceYear ) {

                    # We'll set the initial date to something far in the past...
                    #
                    $priceYear = $priceMonth = $priceDay = 1;
                }

                my $delta = Date::Calc::Delta_Days( $y, $m, $d, $priceYear, $priceMonth, $priceDay );

                if ( $delta > 0 ) {

                    # The next price is in the future.  So whatever current price we have (or don't have) is now the true current price.
                    #
                    last;
                }

                # We're here because the next price is still in the 'past' (relative to the date we're looking at),
                # so keep iterating over prices.
                #
                $self->{_currentExpectedPrice}{$productID}{$priceTypeID} = $self->{_nextExpectedPrice}{$productID}{$priceTypeID};
                $self->{_nextExpectedPrice}{$productID}{$priceTypeID}    = $self->{_expectedPriceData}{$productID}{$priceTypeID}->next();

            }
        } else {

            # We have a current price already.   Keep that unless the next price is no longer in the future.
            # OR until we run past the 'end_date' of the expected price.
            #

            # Note that there may not be a 'current' price (it could be undef).
            #
            my ( $endYear, $endMonth, $endDay );
            if ( defined $self->{_currentExpectedPrice}{$productID}{$priceTypeID} ) {
                ( $endYear, $endMonth, $endDay ) =
                  Common::Util::decodeDateMysql( $self->{_currentExpectedPrice}{$productID}{$priceTypeID}->end_date() );
            }

            if ( !$endYear ) {

                # set the end date to something far into the future.
                #
                $endYear  = 3000;    # If we're still in business in the year 3000, I guess we'll increment this...
                $endMonth = 12;
                $endDay   = 31;
            }
            my $endDelta = Date::Calc::Delta_Days( $y, $m, $d, $endYear, $endMonth, $endDay );

            if ( defined $self->{_nextExpectedPrice}{$productID}{$priceTypeID} ) {
                my ( $priceYear, $priceMonth, $priceDay ) =
                  Common::Util::decodeDateMysql( $self->{_nextExpectedPrice}{$productID}{$priceTypeID}->start_date() );
                if ( !$priceYear ) {

                    # We'll set the initial date to something far in the past...
                    #
                    $priceYear = $priceMonth = $priceDay = 1;
                }

                my $delta = Date::Calc::Delta_Days( $y, $m, $d, $priceYear, $priceMonth, $priceDay );

                # !!! In theory, the '<' is not necessary here...
                #
                if ( $delta <= 0 ) {

                    # The next price is in the past, so it becomes the _potential_ current price.
                    #
                    $self->{_currentExpectedPrice}{$productID}{$priceTypeID} = $self->{_nextExpectedPrice}{$productID}{$priceTypeID};
                    $self->{_nextExpectedPrice}{$productID}{$priceTypeID} = $self->{_expectedPriceData}{$productID}{$priceTypeID}->next();
                } elsif ( $endDelta <= 0 ) {

                    # If we're here, then the current price's end_date is today (or in the past),
                    # and the next expected price is still in the future.
                    # So that means that currently there IS NOT A EXPECTED PRICE for this price type.
                    #

                    $self->{_currentExpectedPrice}{$productID}{$priceTypeID} = undef;
                }
            } elsif ( defined $self->{_currentExpectedPrice}{$productID}{$priceTypeID} ) {

                # So we don't have a next expected price... But we still need to see whether we've gone beyond
                # the end_date of the current price.
                #
                if ( $endDelta <= 0 ) {
                    $self->{_currentExpectedPrice}{$productID}{$priceTypeID} = undef;
                }
            }
        }

        # If there is actually an expected price for this price type, then we'll assume that's the actual expected price to return.
        # (the last one will win if there are more than one...)
        #
        if ( defined $self->{_currentExpectedPrice}{$productID}{$priceTypeID} ) {
            if ( defined $expectedPrice ) {
                Common::Log::Print("WARNING - Multiple expected prices found for date: $y $m $d");
            }

            $expectedPrice = $self->{_currentExpectedPrice}{$productID}{$priceTypeID}->price();
        }
    }

    push @{ $self->{expectedPrices}{$productID} }, $expectedPrice;
}

###
1;    # Play nicely;
###
