#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------
package RPS::SearchCommand;

use URI::Escape;
use lib '/app/tools/common/lib';
use Common::Assert;
use Common::Preference;

use lib '/app/tools/appuser/lib';

use lib '/app/tools/rps/lib';
use RPS::Pagination;
use RPS::AlphabeticalPagination;

use RPS::Command;
use base 'RPS::Command';

use constant kDefaultPageSize => 30;

# jpk - I'm afraid I don't like this implementation very much.
# I don't like the way this code assumes that the way to get the
# collection of items is to take the db class name and tack ::GetAll onto it.
# This is fragile, and introduces a magic hidden dependency between this class
# and every DB::Item class.   In other words, it assumes an interface exists that
# belongs to a class outside this class' scope.   It's bound to cause problems.
#
# I've gone ahead and modified this class somewhat to allow these get and search methods to
# be passed in as arguments, but I still don't like the design very much...
#
# At some point, I'd like to refactor this to be more object-oriented.  Rather that
# use function references (symbolic or otherwise), it would be better to have the
# SearchCommand class provide a set of virtual methods that get called to provide
# these collections - Then classes that descend from SearchCommand could override
# these methods to provide this in anyway they see fit.  That would be a lot more flexible.
#
# !!! Actually, it looks like the DB::Item refactoring I did has accomplished most of this.
# !!! Search and GetAll are now class methods in DB:Item, and those have been refactored to use the '->' notation.
# !!! This allows me to invoke them given just the class name, and allows inheritance to happen
# !!! even in a class method. Slick.
# !!! I might even be able to make the class 'Search' method generic, and move that into
# !!! the base class just like GetAll. Maybe.
#

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

	# Give any inherited methods the first crack at this.
	# (This checks for login and basic access permissions).
	#
	my $response = $self->SUPER::execute();
	return $response if $response;

	my $dbClass = $args{dbClass};
	my $xmlClass = $args{xmlClass};
	my $xmlTag = $args{xmlTag};
	my $loadSubs = $args{loadSubs};
	my $pageSize = $self->getParam("PageSize") || $args{pageSize} || kDefaultPageSize;
	my $query = $self->{query} || $self->getParam('Query');

#    $self->{xml}{Params} = $self->{params};

	assert($xmlTag);
	assert($xmlClass);
	eval "require $xmlClass";

    $self->{xml}{PageName} = $self->pageName() if ($self->can('pageName'));

    # Provide an HTML-encoded version of the query string, so we can easily build links that
    # won't break if there are 'forbidden' characters.
    #
    if ($query)
    {
        $self->{xml}{EncodedQuery} = URI::Escape::uri_escape_utf8($query);
    }

	# let them pass in a collection of their own
	#
	my $collection;
	if($args{collection})
	{
		$collection = $args{collection};
	}
	else
	{
		assert($dbClass, "Missing required dbClass arg");
		eval "require $dbClass";

		if($query)
		{
			# Strip leading and trailing white space
			$query =~ s/^\s+//g;
			$query =~ s/\s+$//g;

			# if(validSearchTerm($searchParam))
            my $searchMethod = 'Search';
            if ($args{searchMethod})
            {
                $searchMethod = $args{searchMethod};
            }
			if(1)
			{
                $collection = $dbClass->$searchMethod($query);
			}
			else
			{
				# TODO: not sure how to handle this
			}
		}
		else
		{
            my $getAllMethod = 'GetAll';
            if ($args{getAllMethod})
            {
                $getAllMethod = $args{getAllMethod};
            }
            $collection = $dbClass->$getAllMethod();
#            $collection = $dbClass->GetAll();
		}

	}

	if(defined $collection)
	{
		assert($xmlClass, "Missing required xmlClass arg");
		eval "require $xmlClass";

		# the new way to do this is to set the pagination parameters on the collection,
		# then we can create the pagination object by referencing the "totalSize()" of the collection.
		# By setting the pagination parameters in the collection before referencing any of its
		# internal properties we save it from doing a "select * from"
		#
		if( $self->getParam( "Page" ) ) {
		    $collection->setPagination(pageIndex => $self->getParam( "Page" ), pageSize => $pageSize);
		} else {
		    my $recordIndex = $self->getParam("Record") || 1;
		    $collection->setPagination(recordIndex => $recordIndex, pageSize => $pageSize);
		}

		# the pagination occurs internally in the collection object
		#
		my @records;

		Common::Log::Debug( $collection );
		while ($collection->hasNext())
		{
			my $data = $collection->next();
			push @records, $xmlClass->new(_dbItem => $data, loadSubs => $loadSubs);
		}

		$self->{xml}{$xmlTag} = \@records if( @records );
		$self->{xml}{Pagination} = RPS::Pagination->new(total => $collection->totalSize(),
														recordNum => $collection->{_record} ,
														pageSize => $pageSize);
	}

	return undef;
}

sub totalCount {
	my $self = shift;

	if( $self->{xml}->{Pagination} ) {
	    return $self->{xml}->{Pagination}->Total;
	} else {
		return 0;
	}
}

sub validSearchTerm
{
	my $term = shift;

	# I think it's safe to require at least 3
	# alphanumeric characters
	#
	my $alphaTerm;
	($alphaTerm = $term) =~ s/[^a-zA-Z0-9]//g;
	return 0 if(length($alphaTerm) < 3);


	return 1;
}


sub setPagesize {
	my $self = shift;

	# Check if the page size was passed in
	my $pagesize = $self->getParam( $self->pageSizeParamName() );

	# If not see if a cookie exists
	$pagesize = Common::Preference::GetValue( $self->pageSizePrefName() )
	    unless( $pagesize );

	# Finally set it to the default
	$pagesize = $self->defaultPageSize() unless( $pagesize );

	# Now set the new page size preference
	my $setMethod = $self->pageSizeMethod();
	&$setMethod( $self->pageSizePrefName(),  $pagesize );

	return $pagesize;
}

#
# Overloadable constants for defining the search page preferences
#
sub defaultPageSize   { 25 };
sub pageSizeParamName { 'PageSize' };
sub pageSizePrefName  { 'pagesize' };
sub pageSizeMethod    { 'Common::Preference::SetPersistentCookie' };

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