#################################################################################
# Ikonboard v3 by Jarvis Entertainment Group, Inc.
#
# No parts of this script can be used outside Ikonboard without prior consent.
#
# More information available from <ib-license@jarvisgroup.net>
# (c)2001 Jarvis Entertainment Group, Inc.
# 
# http://www.ikonboard.com
#
# Please Read the license for more information
#
#
# Search Functions.
#
#################################################################################
package Search::API::api_functions;
use strict;

# Import AnyDBM_File
BEGIN { @AnyDBM_File::ISA = qw(DB_File GDBM_File NDBM_File SDBM_File) }
use AnyDBM_File;

use vars qw(@ISA $VERSION $CONN $TYPE $error %DEBUG %GBL $INFO @r_IGNORE);

# Set up the flock settings
if ($^O ne 'MacOS' && ($^O ne 'MSWin32' || !Win32::IsWin95())) {
    use Fcntl qw(:DEFAULT :flock);
    $INFO->{'FLOCK'} = 1;
}

sub get_new {
    my $IN = { DB       => "",
               DATE     => "",
               FORUMS   => "",
               LIMIT    => 200,
               FILE_KEY => "",
               @_,
             };
    
    my $db   = $IN->{DB};

    my $got_results = 0;

    open SEARCH, ">".$iB::INFO->{'DB_DIR'}.'Temp/Searches'."/$IN->{FILE_KEY}" or die $!;
    print SEARCH time."\n";
    print SEARCH "new\n";
    print SEARCH "new\n";
    print SEARCH "new\n";
    
    #lock the DBM file
    $db->lock_table("forum_topics");
    
    for my $forum_id (@{$IN->{FORUMS}}) {
        # Open up each forums topic list and check for new matches
        # Note that in DBM, each forum has it's own topic file.
        
        #Open the DBM file
        my $file = $db->{'base_dir'} . "forum_topics/forum_topics-$forum_id.db";
    
        tie (my %DB, $AnyDBM_File::ISA[0], $file, O_RDWR|O_CREAT, 0777) || die "Can't open file ($file) for reading. $!";
        #Here we go loopy loooo....
        my ($k, $v);
	    while (($k, $v) = each(%DB)) {
	        # Do we have enough results?
	        if ($got_results >= $IN->{LIMIT}) {
	            last;
	        }
	        @_ = split (/\|\^\|/, $v);
	        if ($_[8] > $IN->{DATE}) {
	            #Add it to the text file
	            print SEARCH "$_[0]+$_[15],";
	            #Increment our number of got results
	            ++$got_results;
	        }
	    }
	    untie %DB;
	    undef %DB;
    }
    
    # Release the lock
    $db->release_lock("forum_topics");
    
    print SEARCH "\n$got_results";
    
    close SEARCH;

    # return the $got_results flag to indicate matches found or not
    
    return $got_results;
    
}


######################################################
# Prepare, execute and store the TOPIC_IDs of the returned
# query.
# Return the amount of matching topics
######################################################

sub run_query {
    my $IN = { 
                DB          => "",
                KEYWORDS    => "",
                PRUNE       => "",
                PRUNE_TYPE  => "",
                FORUMS      => [],
                SEARCH_IN   => "",
                SEARCH_TYPE => "",
                LIMIT       => 200,
                FILE_KEY    => "",
                @_
             };
             
    # Set a more friendly db handler.
    
    my $db   = $IN->{DB};
    
    my $got_results = 0;
    
    my $eval = "(";
    
    while ($IN->{KEYWORDS} =~ m!(^|and|or)\s{1,}(\S+?)\s{1,}!isg) {
        if ($2 ne '') {
            my $keyword = $2;
            my $any_op  = $1;
            
            # Figure out if we're matching a word boundry, or we're using a wild card match
            $keyword = '(^|\s{1,})'.$keyword unless ($keyword =~ /^%/);
            $keyword = $keyword.'($|\s{1,})' unless ($keyword =~ /%$/);
            # Swop SQL's % to a POSIX regexp .*
            $keyword =~ s:%:\.\*:g;
            # Make quotes safe (addslashes :D)
            $keyword =~ s:/:\\/:g;
            # make $any_op spaced
            $any_op = " ".lc($any_op)." ";
                          
            if ($IN->{SEARCH_IN} eq 'Post' or $iB::IN{'type'} eq 'name') {
                $eval .= $IN->{SEARCH_TYPE} eq 'post' ? $any_op."\$row[5] =~ /$keyword/ig" : $any_op."\$row[2] =~ /$keyword/ig";
            }
            elsif ($IN->{SEARCH_IN} eq 'titles') {
                $eval .= $any_op."\$row[4] =~ /$keyword/ig";
            }
            elsif ($IN->{SEARCH_IN} eq 'all') {
                $eval .= $any_op."(\$row[4] =~ /$keyword/ig or \$row[5] =~ /$keyword/i)";
            } else {
                # Must be a post only search then!
                $eval .= $any_op."\$row[5] =~ /$keyword/ig";
            }
        }
    }

    $eval .= ")";


    # Add on the prune arg if needed
    if ($IN->{PRUNE} > 0) {
        my $gt_lt = $IN->{PRUNE_TYPE} eq 'older' ? "<" : ">";
        my $time = time - ($IN->{PRUNE} * 86400);
        $eval .= " and (\$row[3] $gt_lt '$time')";
    }

    open SEARCH, ">".$iB::INFO->{'DB_DIR'}.'Temp/Searches'."/$IN->{FILE_KEY}" or die $!;
    print SEARCH time."\n";
    print SEARCH "$IN->{KEYWORDS}\n";
    print SEARCH "$IN->{SEARCH_IN}\n";
    print SEARCH "$IN->{SEARCH_TYPE}\n";


    #lock the DBM file
    $db->lock_table("search_log");
    
    my $got_topics = {};
    
    for my $forum_id (@{$IN->{FORUMS}}) {
        # Open up each forums topic list and check for new matches
        # Note that in DBM, each forum has it's own topic file.
        
        #Open the DBM file
        my $file = $db->{'base_dir'} . "search_log/search_log-$forum_id.db";
        tie (my %DB, $AnyDBM_File::ISA[0], $file, O_RDWR|O_CREAT, 0777) || die "Can't open file ($file) for reading. $!";
        #Here we go loopy loooo....
        my ($k, $v);
	    while (($k, $v) = each(%DB)) {
	        # Do we have enough results?
	        if ($got_results >= $IN->{LIMIT}) {
	            last;
	        }
	        my @row = split (/\|\^\|/, $v);
	        next if $got_topics->{ $row[7] };
	        my $code = qq~ 
	                if ($eval) {
	                    print SEARCH "\$row[7]+\$row[6],";
	                    ++\$got_results;
	                    ++\$got_topics->{ \$row[7] };
	                }
	             ~;
	        eval $code;
	    }
   
	    untie %DB;
	    undef %DB;
    }
    
    # Release the lock
    $db->release_lock("search_log");
    
    print SEARCH "\n$got_results";
    close SEARCH;

    # return the $got_results flag to indicate matches found or not
    
    return $got_results;
    
}


sub parse_results {
    my $IN = {  DB           => "",  #var
                START        => "",  #int
                END          => "",  #int
                TOPIC_STRING => "",  #string
                KEYWORDS     => "",
                @_,
             };
             
    my $print;
    my $start = $IN->{START};
    my $end   = $IN->{END};
    my $db    = $IN->{DB};
    my $tbl   = $db->prefix.'forum_info';
    
    #Tidy up the keywords
    
    $IN->{KEYWORDS} =~ s!^\s+!!;
    $IN->{KEYWORDS} =~ s!\s+$!!g;
    $IN->{KEYWORDS} =~ s!\s!\+!g;
    
    my $forum_hash = {};
    
    my $forums = $db->query( TABLE  => 'forum_info' );
    
    for my $f (@{$forums}) {

        if ($f->{'FORUM_PROTECT'}) {
            next unless $iB::COOKIES->{ $iB::INFO->{'COOKIE_ID'}.'iBForum' . $f->{'FORUM_ID'} } eq $f->{'FORUM_PROTECT'};
        }
        
        if ($f->{'FORUM_VIEW_THREADS'} ne '*') {
            next unless grep { $_ == $iB::MEMBER->{'MEMBER_GROUP'} } (split /,/,$f->{'FORUM_VIEW_THREADS'});
        }

        $forum_hash->{ $f->{'FORUM_ID'} } = { 'FORUM_ID'   => $f->{'FORUM_ID'},
                                              'FORUM_NAME' => $f->{'FORUM_NAME'},
                                            };
    }
    

    
    # Split the string based on our start and end integers and form
    # a SQL usable IN statement
    
    my $topic_ids = {};
    my $i = 0;
    for (split/,/, $IN->{TOPIC_STRING}) {
        my ($tid, $fid) = split/\+/;
        ++$i;
        next unless $i >= $start;
        last if     $i >= $end;
        unless (exists $topic_ids->{ $fid }) {
            $topic_ids->{ $fid } = [];
        }
        push @{$topic_ids->{ $fid }}, $tid;
    }

    # Define an array to hold the topics matched for the
    # forum, so we can sort via the last post date.
    
    my @sort_keys;
    
    $db->lock_table("forum_topics");
    
    for my $f (sort keys %{$topic_ids}) {
        my $file = $db->{'base_dir'} . "forum_topics/forum_topics-$f.db";
        tie (my %DB, $AnyDBM_File::ISA[0], $file, O_RDWR|O_CREAT, 0777) || die "Can't open file ($file) for reading. $!";    
        for (@{$topic_ids->{$f}}) {
            if ($DB{ $_ } ne '') { 
                my @r = split/\|\^\|/, $DB{ $_ };
                push @sort_keys, { "TOPIC_ID"          => $r[0],
                                   "TOPIC_TITLE"       => $r[1],
                                   "TOPIC_DESC"        => $r[2],
                                   "TOPIC_STATE"       => $r[3],
                                   "TOPIC_POSTS"       => $r[4],
                                   "TOPIC_STARTER"     => $r[5],
                                   "TOPIC_START_DATE"  => $r[6],
                                   "TOPIC_LAST_POSTER" => $r[7],
                                   "TOPIC_LAST_DATE"   => $r[8],
                                   "TOPIC_ICON"        => $r[9],
                                   "TOPIC_STARTER_N"   => $r[10],
                                   "TOPIC_LASTP_N"     => $r[11],
                                   "TOPIC_VIEWS"       => $r[14],
                                   "FORUM_ID"          => $r[15],
                                   "APPROVED"          => $r[16],
                                   "TOPIC_AUTHOR_TYPE" => $r[17],
                                   "PIN_STATE"         => $r[18],
                                   "MOVED_TO"          => $r[19],
                                  };
            }
        }
        untie %DB;
        undef %DB;
    }
    
    $db->release_lock("forum_topics");
    
    # Sort by the topic last date
    my @final_keys =  sort { $b->{ $IN->{TOPIC_LAST_DATE} }
                                            <=>
                             $a->{ $IN->{TOPIC_LAST_DATE} } } @sort_keys;
    
    undef @sort_keys;
    

    for my $r (@final_keys) {
        $r->{KEYWORDS}     = $IN->{KEYWORDS};
        $r->{'FORUM_ID'}   = $forum_hash->{ $r->{FORUM_ID} }->{'FORUM_ID'};
        $r->{'FORUM_NAME'} = $forum_hash->{ $r->{FORUM_ID} }->{'FORUM_NAME'};
        $print .= Search::API::api_global::do_row( $r ) if $r->{'TOPIC_TITLE'} ne '';
    }
    undef @final_keys;
    
    return $print;
}



1;