# Business::Shipping::RateRequest - Abstract class for shipping cost rating.
# 
# $Id: RateRequest.pm,v 1.3 2003/08/20 12:58:47 db-ship Exp $
# 
# Copyright (c) 2003 Kavod Technologies, Dan Browning. All rights reserved. 
# 
# Licensed under the GNU Public Licnese (GPL).  See COPYING for more info.
# 

package Business::Shipping::RateRequest;

use strict;
use warnings;

use vars ( '$VERSION' );
#@ISA = ( 'Business::Shipping' );
use base ( 'Business::Shipping' );
$VERSION = do { my @r=(q$Revision: 1.3 $=~/\d+/g); sprintf "%d."."%03d"x$#r,@r };

use Business::Shipping::Debug;
use Cache::FileCache;
use Business::Shipping::CustomMethodMaker
	new_hash_init => 'new',
	boolean => [ 'is_success', 'cache' ],
	hash => [ 'results' ],
	grouped_fields_inherit => [
		required => [ 'shipper' ],
		unique => [ 'shipper' ]
	],
	object => [
		# Right now, each RateRequest has only one shipment.
		# Eventually, maybe we'll use object_list with "shipments()->..." like packages.
		'Business::Shipping::Shipment' => {
			'slot' => 'shipment',
			'comp_mthds' => [ 
				'service', 
				'from_country',
				'to_country', 
				'from_zip',
				'to_zip',
				'packages',
				'default_package',
				'weight',
				'shipper',
			],
		}
	];

#sub get_package { return shift->packages()->[ @_ ]; }

=item $shipment->submit( [%args] )

This method sets some values (optional), generates the request, then parses the
results.

=cut
sub submit
{
	my ( $self, %args ) = @_;
	trace( "( " . uneval( %args ) . " )" );
	$self->init( %args ) if %args;
	$self->_massage_values();
	$self->validate() or return ( undef );
	
	my $cache = Cache::FileCache->new() if $self->cache();
	
	if ( $self->cache() ) {
		trace( 'cache enabled' );	

		my $key = $self->gen_unique_key();
		debug "cache key = $key\n";
		
		my $results = $cache->get( $key );
		if ( $results ) {
			trace( "found cached response, using that." );
			$self->results( $results );
			return 1;
		}
		else {
			trace( 'Cannot find cached results, running request manually, then add to cache.' );
		}
	}
	else {
		trace( 'cache disabled' );
	}
	
	$self->perform_action();
	
	my $results = $self->results();
	
	use Data::Dumper;
	debug 'results = ' . Dumper( $results );
	
	# Only cache if there weren't any errors.
	if ( $self->_handle_response() and $self->cache() ) {	
		trace( 'cache enabled, saving results.' );
		#TODO: Allow setting of cache properties (time limit, enable/disable, etc.)
		my $key = $self->gen_unique_key();
		my $cache = Cache::FileCache->new();
		$cache->set( $key, $results, "2 days" );
	}
	else {
		trace( 'cache disabled, not saving results.' );
	}
	
	return $self->is_success();
}

sub get_unique_hash
{
	my $self = shift;
	
	my %unique;
	
	$unique{ $_ } = $self->$_() for $self->unique();
	$unique{ $_ } = $self->shipment->$_() for $self->shipment->unique();
	
	foreach my $package ( $self->shipment->packages() ) {
		foreach my $package_unique_key ( $package->unique() ) {
			$unique{ 'p1_' . $package_unique_key } = $package->$package_unique_key();
		}
	}
	return %unique;
}

sub gen_unique_key
{
	my $self = shift;
	my %unique = $self->get_unique_hash();
	my @sorted_values = $self->hash_to_sorted_values( %unique ); 
	return join( '|', @sorted_values );
	return;
}

sub hash_to_sorted_values
{
	my $self = shift;
	my ( %hash ) = @_;
	my @sorted_values;
	foreach my $key ( sort keys %hash ) {
		push @sorted_values, ( $hash{ $key } || '' );
	}
	return @sorted_values;
}

sub total_charges
{
	trace '()';
	my $self = shift;
	my $total;
	
	my $shippers = $self->results();
	foreach my $shipper ( keys %$shippers ) {
		debug "\tshipper: $shipper\n";
		
		my $packages = $self->results( $shipper );		
		foreach my $package ( @$packages ) {
			debug3 "\t" . uneval( $package );
			debug "\t\tcharges = " . $package->{ 'charges' } . "\n";
			$total += $package->{ 'charges' };
		}
	}
		
	return $total;
}

sub get_unique_keys
{
	my $self = shift;
	
	# None at the Business::Shipping level.
	my @unique_keys = ();
	
	return( @unique_keys );
}

sub _gen_unique_values
{
	trace '()';
	my ( $self ) = @_;
		
	# Now I need to get unique values for all packages.
	
	my @unique_values;
	foreach my $package ( @{$self->packages()} ) {
		push @unique_values, $package->get_unique_values();
	}
	
	# We prefer 0 in the key to represent 'undef'
	# clean it all up...
	my @new_unique_values;
	foreach my $value ( @unique_values ) {
		if ( not defined $value ) {
			$value = 0;
		}
		push @new_unique_values, $value;
	}

	return( @new_unique_values );
}

#
# This is from 0.04.
# Needs to be made compatible with the new version.
#
sub add_package
{
	my $self = shift;
	trace('called with' . uneval( @_ ) );
	
	my $new = Business::Shipping->new( 'package' => $self->package_subclass_name() );
	
	$new->set( @_ );
	
	# If the passed package has an ID, then use that.
	if ( $new->id() or ( $new->id() and $new->id() == 0 ) ) {
		trace( "Using id in passed package" );
		$self->packages()->[$new->id()] = $new;
		return 1;
	}
		
	# If the "default" package ($self->packages()->[0]) is 
	# still in "default" state (has not yet been updated),
	# then replace it with the passed package.
	trace( 'checking to see if default package is empty...' );
	if ( $self->default_package()->is_empty() ) {
		trace( 'yes, is empty.' );
		$self->packages()->[0] = $new;
		trace( 'done setting up default package.' );
		return 1;	
	}
	trace( 'no, not empty.' );
	
	# Otherwise, add the package in the second slot.
	push( @{$self->packages()}, $new );
	
	return 1;
}

1;
