# Copyright 2002-2008 Josh Clark and Global Moxie, LLC. This code cannot be
# redistributed without permission from globalmoxie.com.  For more
# information, consult your Big Medium license.
#
# $Id: Scheduler.pm 3043 2008-03-31 14:00:38Z josh $

package BigMed::Search::Scheduler;
use strict;
use warnings;
use utf8;
use Carp;
$Carp::Verbose = 1;
use BigMed::JanitorNote;

our @EXPORT = qw(schedule_index schedule_deindex);
use base 'Exporter';

sub schedule_index {
    return _do_update( 'add', @_ );
}

sub schedule_deindex {
    return _do_update( 'remove', @_ );
}

sub _do_update {
    my $action = shift;
    my ( $site, $rpids ) = _gather_ids(@_);
    return if !$site;
    return 1 if !@{$rpids};    #nothing to do

    #remove any existing contradictory schedules for these pages
    my $anti_action = $action eq 'add' ? 'remove' : 'add';
    _remove_existing( $anti_action, $site, $rpids ) or return;
    my $note = BigMed::JanitorNote->new();
    $note->set_site($site);
    $note->set_action("${action}_search_index");
    $note->set_target($rpids);
    return $note->save;
}

sub _gather_ids {
    my ( $site, $page ) = @_;
    my $sid = $site;
    $sid = $site->id if ref $sid eq 'BigMed::Site';
    croak 'site id required to schedule search index updates'
      if !$sid || $sid =~ /\D/ms;

    my $type = ref $page;
    my $rpids;
    if ( $type && index($type,'BigMed::Driver') == 0 ) {    #driver selection
        $page->set_index(0);
        $rpids = [];
        while ( my $rindex = $page->next_index ) {
            push @{$rpids}, $rindex->{id};
        }
        $page->set_index(0);
    }
    else {
        $rpids =
            $type eq 'BigMed::Content::Page' ? [$page->id]
          : $type eq 'ARRAY' ? $page
          : ( $page && $page !~ /\D/ms ) ? [$page]
          : [];
    }
    return ( $sid, $rpids );
}

sub _remove_existing {
    my ( $action, $site, $rpids ) = @_;
    my %remove = map { $_ => 1 } @{$rpids};
    my $select =
      BigMed::JanitorNote->select(
        { site => $site, action => "${action}_search_index" } )
      or return;
    my ( $note, @delete_id );
    while ( $note = $select->next ) {
        my $count = ( my @targets = $note->target );
        my @revised = grep { !$remove{$_} } @targets;
        my $rev_count = @revised;
        if ( !$rev_count ) {    #nothing left
            push @delete_id, $note->id;
        }
        elsif ( $rev_count != $count ) {    #update
            $note->set_target( \@revised );
            $note->save or return;
        }
    }
    return if !defined $note;

    if (@delete_id) {
        $select = $select->select( { site => $site, id => \@delete_id } )
          or return;
        $select->trash_all or return;
    }
    return 1;
}

1;
__END__

=head1 BigMed::Search::Scheduler

Handles scheduling of adding/removing pages to Big Medium's search index

=head1 DESCRIPTION

BigMed::Search::Scheduler schedules pages to be added to the search-engine
index by BigMed::Janitor via Big Medium's maintenance script.

The module exports two methods, C<schedule_index> and C<schedule_deindex>,
which respectively add and remove pages from the search index. Behind
the scenes, these methods leave BigMed::JanitorNote objects telling
BigMed::Janitor to index/deindex the pages.

=head1 SYNOPSIS

    use BigMed::Search::Scheduler;

    #schedule pages to be indexed
    schedule_index( $site_id_or_obj, $page_obj )       #via page object
      or BigMed::Error->error_stop;
    schedule_index( $site_id_or_obj, $page_id )        #via page id
      or BigMed::Error->error_stop;
    schedule_index( $site_id_or_obj, \@page_ids )      #array of page ids
      or BigMed::Error->error_stop;
    schedule_index( $site_id_or_obj, $page_selection ) #via selection
      or BigMed::Error->error_stop;

    #schedule pages to be removed from index
    schedule_deindex( $site_id_or_obj, $page_obj )       #via page object
      or BigMed::Error->error_stop;
    schedule_deindex( $site_id_or_obj, $page_id )        #via page id
      or BigMed::Error->error_stop;
    schedule_deindex( $site_id_or_obj, \@page_ids )      #array of page ids
      or BigMed::Error->error_stop;
    schedule_deindex( $site_id_or_obj, $page_selection ) #via selection
      or BigMed::Error->error_stop;

=head1 EXPORTED METHODS

=head2 schedule_index

    schedule_index( $site_id_or_obj, $page_obj );       #via page object
    schedule_index( $site_id_or_obj, $page_id );        #via page id
    schedule_index( $site_id_or_obj, \@page_ids );      #array of page ids
    schedule_index( $site_id_or_obj, $page_selection ); #via selection

Schedules the page(s) in the second argument to be added (or updated)
in the search index for the site in the first argument. If the page(s)
were previously scheduled to be removed, those removal actions are replaced.

Returns true on success and false on error (also adding a message to the
BigMed::Error queue).

The site argument can be either a BigMed::Site object or a site id. The
page argument can be any of these:

=over 4

=item * A BigMed::Content::Page object

=item * Id of a BigMed::Content::Page object

=item * Array of BigMed::Content::Page ids

=item * Selection object containing BigMed::Content::Page objects

=back

=head2 schedule_deindex

    schedule_deindex( $site_id_or_obj, $page_obj );       #via page object
    schedule_deindex( $site_id_or_obj, $page_id );        #via page id
    schedule_deindex( $site_id_or_obj, \@page_ids );      #array of page ids
    schedule_deindex( $site_id_or_obj, $page_selection ); #via selection

Schedules the page(s) in the second argument to be removed from
the search index for the site in the first argument. If the page(s)
were previously scheduled to be indexed, those indexing actions are replaced.

Returns true on success and false on error (also adding a message to the
BigMed::Error queue).

As with C<schedule_index>, the site argument can be either a BigMed::Site
object or a site id. The page argument can be any of these:

=over 4

=item * A BigMed::Content::Page object

=item * Id of a BigMed::Content::Page object

=item * Array of BigMed::Content::Page ids

=item * Selection object containing BigMed::Content::Page objects

=back

=head1 SEE ALSO

=over 4

=item * BigMed::Janitor

=item * BigMed::Search

=back

=head1 AUTHOR & COPYRIGHTS

This module and all Big Medium modules are copyright Josh Clark
and Global Moxie. All rights reserved.

Use of this module and the Big Medium content
management system are governed by Global Moxie's software licenses
and may not be used outside of the terms and conditions outlined
there.

For more information, visit the Global Moxie website at
L<http://globalmoxie.com/>.

Big Medium and Global Moxie are service marks of Global Moxie
and Josh Clark. All rights reserved.

=cut

