# -*- cperl -*-
package Celaenito::Httpd::Search;
use strict;
use warnings;
use HTTP::Status;
use Pager;
use Smart::Comments;
use Celaenito::Permission;
use Celaenito::Template;
use Celaenito::DB;
use Celaenito::Curi;
use Celaenito::Httpd;
use Celaenito::Config;
use Celaenito::HyperEstraier;
use Date::Parse;
use Time::HiRes qw(gettimeofday tv_interval);

sub handler {
    my ($req, $resp) = @_;

    not CAN_SEE($req) and
      return need_auth_handler(@_);

    # クエリ中に [ と ] で圍まれた部分があれば、その部分は
    # 屬性檢索條件式と見做す。
    my $q = $req->uri->query_param('q');
    defined $q or $q = '';
    $q =~ s/^\s*|\s*$//g;
    
    my $phrase = '';
    my @words;
    my $cond = Condition->new;
    foreach my $part (split /(".*?"|\[.*?\])/, $q) {
        defined $part or next;

        if ($part =~ m/^"(.*?)"$/) {
            $phrase .= qq{ "$part"};
            push @words, $part;
        }
        elsif ($part =~ m/^\[(.*?)\]$/) {
            my $attr = $1;
            
            if ($attr =~ m/\s/) {
                $cond->add_attr($attr);
            }
            else {
                $phrase .= " [$attr]";
            }
        }
        else {
            $part =~ s/^\s*|\s*$//g;
            
            if (length $part) {
                $phrase .= ' ' . $part;

                foreach my $word (split /\s+/, $part) {
                    $word =~ m/^[\x21-\x2f\x3a-\x40\x5b-\x60\x7b-\x7e]+$/ and next;
                    
                    push @words, $word;
                }
            }
        }
    }

    if (length $phrase) {
        $cond->set_phrase($phrase);
        $cond->set_options($HyperEstraier::Condition::SIMPLE);
    }
    else {
        $cond->set_phrase('[UVSET]');
    }

    my $t = {
        title    => config('ui.title'),
        q        => $q,
        estraier => defined EST,
    };

    if (length $q) {
        EST->read(
            sub {
                my $E = shift;

                my $before = [gettimeofday];
                my $result = $E->search($cond);
                
                $t->{result} = {
                    duration => tv_interval($before),
                    urls     => $result->doc_num,
                };

                my $pager = Pager->new(
                    fetch => {
                        code => sub {
                            my ($begin, $length, $code_show) = @_;
                            foreach my $i ($begin .. ($begin + $length - 1)) {
                                my $id = $result->get_doc_id($i);
                                my $doc = $E->get_doc($id, 0);

                                $code_show->($doc);
                            }
                        },
                    },
                    count => {
                        code => sub {
                            $result->doc_num;
                        },
                    },
                    page_to_show   => scalar($req->uri->query_param('page')) || 1,
                    pagenums_limit => 10,
                    items_per_page => scalar($req->uri->query_param('limit')) || 20,
                    show_meta => sub {
                        my %meta = @_;
                        $t->{result}{page}      = $meta{page};
                        $t->{result}{last_page} = $meta{last_page};
                        $t->{result}{pages}     = [$meta{pagelink_begin} .. $meta{pagelink_end}];
                        
                    },
                    show_content => sub {
                        my $doc = shift;

                        $t->{result}{entries} or
                          $t->{result}{entries} = [];

                        my $ent = {};
                        push @{$t->{result}{entries}}, $ent;

                        $ent->{celaenito_url} =
                          celaenito_uri(
                              host_uri => $req->uri,
                              orig_uri => $doc->attr('@uri'),
                              time     => 'latest',
                             )->as_string;

                        $ent->{title} = $doc->attr('@title');
                        $ent->{label} = $doc->attr('#label');
                        $ent->{url}   = $doc->attr('@uri');

                        my $snip = $doc->make_snippet(\@words, 300, 80, 80);
                        $ent->{snippet} = '';
                        foreach my $line (split /\r?\n|\r/, $snip) {
                            if (not length $line) {
                                $ent->{snippet} .= ' ... ';
                            }
                            elsif ($line =~ m/^(.+?)\t(.+)$/) {
                                $ent->{snippet} .= "<em>$1</em>";
                            }
                            else {
                                $ent->{snippet} .= $line;
                            }
                        }
                        if (length $ent->{snippet}) {
                            $ent->{snippet} .= " ...";
                        }

                        my $u_info = DB->selectrow_hashref(q{
                            SELECT urlid,
                                   UNIX_TIMESTAMP(last_modified) AS last_modified
                              FROM target_url
                             WHERE url = ?
                        }, undef, $ent->{url});

                        if ($u_info) {
                            $ent->{urlid} = $u_info->{urlid};
                            
                            if (str2time($doc->attr('@mdate')) <
                                  $u_info->{last_modified}) {
                                $ent->{outdated} = 1;
                            }
                        }
                        else {
                            $ent->{deleted} = 1;
                            EST->delete($ent->{url});
                        }
                    },
                   );
                $pager->execute;
            });
    }

    my $out = '';
    TMPL->process('search.tt', $t, \$out);

    $resp->content($out);
    return RC_NOT_FOUND;
}

1;
