<?page
title=><?_ml .title _ml?>
head=><?_code return LJ::robot_meta_tags(); _code?>
body<=

<?_code
{
    use strict;
    use vars qw(%GET %POST);

    LJ::set_active_crumb('searchinterests');

    return "<?badinput?>" unless LJ::text_in(\%GET) && LJ::text_in(\%POST);

    my $did_post = LJ::did_post();

    my $remote = LJ::get_remote();

    my $table = sub { $_[0]->{'journaltype'} eq 'C' ? 'comminterests' : 'userinterests' };

    if (!$did_post && $GET{'view'} eq "popular") {
        return $ML{'.popular.disabled'} if $LJ::DISABLED{'interests-popular'};
        my $ret = "";
        $ret .= "<?h1 $ML{'.popular.head'} h1?><?p $ML{'.popular.text'} p?>";

        my $dbr = LJ::get_db_reader();
        my $sth = $dbr->prepare("SELECT statkey, statval FROM stats WHERE ".
                                "statcat=? ORDER BY statval DESC, statkey ASC");
        $sth->execute('pop_interests');
        return "Sorry, interest data currently unavailable." unless $sth->rows();

        $ret .= "<p><table><tr><td><b>$ML{'.interest'}</b></td><td><b>$ML{'.count'}</b></td></tr>";
        while (my ($int, $count) = $sth->fetchrow_array)
        {
            next if ($count == 1);
            LJ::text_out(\$int);
            my $eint = LJ::eurl($int);
            $ret .= "<tr><td><a href=\"/interests.bml?int=$eint\">$int</a></td><td>$count</td></tr>";
        }
        $ret .= "</table>";
        return $ret;
    }

    if ((!$did_post && $GET{'mode'} eq "add" && $GET{'intid'}) ||
        ($did_post && $POST{'mode'} eq "add" && $POST{'intid'})) {

        my $intid = $did_post ? $POST{'intid'}+0 : $GET{'intid'}+0;

        my $ret;
        unless ($remote) {
            $ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'.error.add.mustlogin'} p?>";
            return $ret;
        }

        # force them to either come from the interests.bml page, or have posted the request.
        # if both fail, ask them to confirm with a post form.

        my $dbr = LJ::get_db_reader();

        unless ($did_post || BML::get_client_header('Referer') =~ /^\Q$LJ::SITEROOT\E\/interests\.bml\?/)
        {
            my ($int) = $dbr->selectrow_array("SELECT interest FROM interests WHERE intid=?", undef, $intid);
            LJ::text_out(\$int);

            $ret .= "<?h1 $ML{'.add.confirm.head'} h1?>";
            $ret .= "<?p " . BML::ml(".add.confirm.text", {'interest' => $int});
            $ret .= "<form method='post' action='interests.bml'><div align='center'>";
            $ret .= LJ::html_hidden('mode' => 'add', 'intid' => $intid);
            $ret .= LJ::html_submit(undef, BML::ml(".add.btn.text", {'interest' => $int}));
            $ret .= "</div></form> p?>";
            return $ret;
        }

        my $rints = LJ::get_interests($remote);
        my $count = scalar(@$rints);

        if ($count >= 150) {
            $ret .= "<?h1 $ML{'.add.toomany.head'} h1?><?p " .BML::ml(".add.toomany.text", {'maxinterests' => "150"}) ." p?>";
            return $ret;
        }

        my $dbh = LJ::get_db_writer();
        my $uitable = $table->($remote);
        $dbh->do("INSERT INTO $uitable (userid, intid) VALUES (?, ?)",
                 undef, $remote->{'userid'}, $intid);
        LJ::memcache_kill($remote, "intids");
        unless ($dbh->err) {
            $dbh->do("UPDATE interests SET intcount=intcount+1 WHERE intid=?", undef, $intid);
        }

        # if a community, remove any old rows from userinterests
        if ($remote->{'journaltype'} eq 'C') {
            $dbh->do("DELETE FROM userinterests WHERE userid=?", undef, $remote->{'userid'});
        }

        $ret .= "<?h1 $ML{'.add.added.head'} h1?><?p $ML{'.add.added.text'} p?>";
        return $ret;
    }

    if (!$did_post && $GET{'mode'} eq "findsim_do") {
        return $ML{'error.tempdisabled'} if $LJ::DISABLED{'interests-findsim'};

        return $ML{'.findsim_do.account.notallowed'} unless LJ::get_cap($remote, "findsim");

        my $ret = "";
        my $u = LJ::load_user($GET{'user'});
        return "<?h1 $ML{'Error'} h1?><?p $ML{'error.username_notfound'} p?>" unless $u;

        my @ints;
        my %intcount;
        my $dbr = LJ::get_db_reader();
        my $sth = $dbr->prepare("SELECT i.intid, i.intcount FROM userinterests ui, interests i ".
                                "WHERE ui.userid=? AND ui.intid=i.intid");
        $sth->execute($u->{'userid'});
        while (my ($intid, $count) = $sth->fetchrow_array) {
            push @ints, $intid;
            $intcount{$intid} = $count || 1;
        }
        unless (@ints) {
            my $msg = BML::ml('.findsim_do.notdefined', { 'user' => LJ::ljuser($u) });
            return "<?h1 $ML{'Error'} h1?><?p $msg p?>";
        }

        my %pt_count;
        my %pt_weight;
        foreach my $int (@ints) {
            # the magic's in this limit clause.  that's what makes this work.  perfect 
            # results?  no.  but who cares if somebody that lists "music" or "women"
            # doesn't get an extra point towards matching you.  we care about more unique interests.
            my $sth = $dbr->prepare("SELECT userid FROM userinterests WHERE intid=? LIMIT 500");
            $sth->execute($int);
            while (my $uid = $sth->fetchrow_array) {
                next if $uid == $u->{'userid'};
                $pt_weight{$uid} += (1 / log($intcount{$int}+1));
                $pt_count{$uid}++;
            }
        }

        my %magic;  # balanced points
        foreach (keys %pt_count) {
            $magic{$_} = $pt_weight{$_}*10 + $pt_count{$_};
        }

        my @matches = sort { $magic{$b} <=> $magic{$a} } keys %magic;
        if (@matches > 150) { @matches = @matches[0..149]; }
        my $sth = $dbr->prepare("SELECT userid, user FROM useridmap WHERE userid IN (" . join(",",@matches) . ")");
        $sth->execute;
        my %username;
        while (my ($id, $name) = $sth->fetchrow_array) {
            $username{$id} = $name;
        }

        unless (@matches) {
            return "<?h1 $ML{'.findsim_do.nomatch.head'} h1?><?p " .BML::ml(".findsim_do.nomatch.text", {'user' => LJ::ljuser($u)}) ." p?>";
        }

        $ret .= "<?h1 $ML{'.findsim_do.similar.head'} h1?><?p " .BML::ml(".findsim_do.similar.text", {'user' => LJ::ljuser($u)}) ." p?>";

        $ret .= "<p><table cellpadding='3'><tr valign='bottom'><td><b>#</b></td><td width='250'><b>$ML{'User'}</b></td><td><b>$ML{'.findsim_do.magic'}</b></td></tr>";
        my $count;
        foreach my $uid (@matches)
        {
            $count++;
            $ret .= "<tr><td>$count</td><td>";
            $ret .= LJ::ljuser($username{$uid});
            $ret .= sprintf("</td><td>%.3f</td></tr>", $magic{$uid});
        }
        $ret .= "</table></p>";

        $ret .= "<?h1 $ML{'.findsim_do.magic.head'} h1?><?p $ML{'.findsim_do.magic.text'} p?>";
        return $ret;
    }

    if (!$did_post && $GET{'mode'} eq "enmasse")
    {
        return LJ::bad_input($ML{'.error.enmasse.mustlogin'}) unless $remote;

        my $authas = $GET{'authas'} || $remote->{'user'};
        my $u = LJ::get_authas_user($authas);
        return LJ::bad_input(BML::ml('.error.enmasse.noaccess', {'user' => LJ::ljuser($authas)})) unless $u;

        my $altauthas = $remote->{'user'} ne $u->{'user'};
        my $getextra = $altauthas ? "?authas=$u->{'user'}" : '';

        my $userid = $u->{'userid'};
        my $username = $u->{'user'};
        my $fromu = LJ::load_user($GET{'fromuser'} || $username);

        my %uint;

        my %fromint;
        my $fints = LJ::get_interests($fromu);
        foreach (@$fints) {
            $fromint{$_->[1]} = $_->[0]+0;
        }

        return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.nointerests'} p?>" unless %fromint;

        my $ret = "<?p <form method='get' action='interests.bml'>";
        $ret .= LJ::html_hidden(mode => 'enmasse', fromuser => $fromu->{'user'});
        $ret .= LJ::make_authas_select($remote, { 'authas' => $GET{'authas'} });
        $ret .= "</form> p?><form method='post' action='interests.bml$getextra'>";
        $ret .= "<?h1 $ML{'.enmasse.header'} h1?><?p ";

        if ($u->{'userid'} == $fromu->{'userid'}) {
            %uint = %fromint;
            $ret .= $ML{'.enmasse.body.you'};
        } else {
            my $in = join (",", map { $fromint{$_} } keys %fromint);

            my $uints = LJ::get_interests($u);
            foreach (@$uints) {
                $uint{$_->[1]} = $_->[0];
            }

            if ($altauthas) {
                $ret .= BML::ml('.enmasse.body.other_authas', { 'user' => LJ::ljuser($fromu),
                                                                'target' => LJ::ljuser($u) });
            } else {
                $ret .= BML::ml('.enmasse.body.other', { 'user' => LJ::ljuser($fromu) });
            }
        }

        $ret .= " p?><div style='margin-left: 40px; margin-top: 20px;'>";
        $ret .= "<table cellpadding='0' cellspacing='0' border='0' width='100%'>";
        my @fromintsorted = sort keys %fromint;
        my $cols = 3;
        my $rows = int((scalar(@fromintsorted) + $cols - 1) / $cols);
        for (my $i = 0; $i < $rows; $i++) {
            $ret .= "<tr valign='middle'>";
            for (my $j = 0; $j < $cols; $j++) {
                my $index = $rows * $j + $i;
                if ($index < scalar(@fromintsorted)) {
                    my $checked = $uint{$fromintsorted[$index]} ? 1 : undef;
                    $ret .= "<td align='left' nowrap='nowrap'>";
                    $ret .= LJ::html_check({name     => "int_$fromint{$fromintsorted[$index]}",
                                            id       => "int_$fromint{$fromintsorted[$index]}",
                                            selected => $checked,
                                            value    => 1});
                    my $bold1 = $checked ? "<strong>" : "";
                    my $bold2 = $checked ? "</strong>" : "";
                    $ret .= "&nbsp;<label for='int_$fromint{$fromintsorted[$index]}'>";
                    $ret .= "$bold1$fromintsorted[$index]$bold2</label></td>";
                } else {
                    $ret .= "<td></td>";
                }
            }
            $ret .= "</tr>";
        }
        $ret .= "</table></div>";

        $ret .= LJ::html_hidden('mode',        'enmasse_do');
        $ret .= LJ::html_hidden('fromuser',    $fromu->{'user'});
        $ret .= LJ::html_hidden('allintids',   join (",", values %fromint));

        $ret .= "<?h1 $ML{'.finished.header'} h1?><?p $ML{'.finished.about'} p?><?standout ";
        $ret .= LJ::html_submit(undef, $ML{'.finished.save_button'}) . " standout?></form>";
        return $ret;
    }

    if ($did_post && $POST{'mode'} eq "enmasse_do") {
        return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.enmasse.mustlogin'} p?>" unless $remote;

        my $authas = $GET{'authas'} || $remote->{'user'};
        my $u = LJ::get_authas_user($authas);
        return LJ::bad_input($ML{'.error.noauth'}) unless $u;

        my $uitable = $table->($u);
        my %uint;
        my $intcount = 0;

        my $uints = LJ::get_interests($u);
        foreach (@$uints) {
            $uint{$_->[0]} = $_->[1];  # uint{intid} = interest
            $intcount++;
        }

        my @fromints = map { $_+0 } split (/\s*,\s*/, $POST{'allintids'});
        my @todel;
        my @toadd;
        foreach my $fromint (@fromints) {
            next unless $fromint > 0;    # prevent adding zero or negative intid
            push (@todel, $fromint) if  $uint{$fromint} && !$POST{'int_'.$fromint};
            push (@toadd, $fromint) if !$uint{$fromint} &&  $POST{'int_'.$fromint};
        }
        my ($deleted, $added, $toomany) = (0, 0, 0);
        if (@todel) {
            my $intid_in = join(",", @todel);
            my $dbh = LJ::get_db_writer();
            $dbh->do("DELETE FROM $uitable WHERE userid=? AND intid IN ($intid_in)",
                     undef, $u->{'userid'});
            $dbh->do("UPDATE interests SET intcount=intcount-1 WHERE intid IN ($intid_in)");
            $deleted = 1;
        }
        if (@toadd) {
            if ($intcount + scalar @toadd > 150) {
                $toomany = 1;
            } else {
                my $dbh = LJ::get_db_writer();
                my $sqlp = "(?,?)" . (",(?,?)" x (scalar(@toadd) - 1));
                my @bindvars = map { ($u->{'userid'}, $_) } @toadd;
                $dbh->do("REPLACE INTO $uitable (userid, intid) VALUES $sqlp", undef, @bindvars);

                my $intid_in = join(",", @toadd);
                $dbh->do("UPDATE interests SET intcount=intcount+1 WHERE intid IN ($intid_in)");
                $added = 1;
            }
        }

        # if a community, remove any old rows from userinterests
        if ($u->{'journaltype'} eq 'C') {
            my $dbh = LJ::get_db_writer();
            $dbh->do("DELETE FROM userinterests WHERE userid=?", undef, $u->{'userid'});
        }

        my $ret = "<?h1 $ML{'.results.header'} h1?><?p ";
        if ($deleted) {
            $ret .= $added   ? $ML{'.results.both'}
                  : $toomany ? BML::ml('.results.del_and_toomany', {'intcount' => 150})
                  : $ML{'.results.deleted'};
        } else {
            $ret .= $added   ? $ML{'.results.added'}
                  : $toomany ? BML::ml('.results.toomany', {'intcount' => 150})
                  : $ML{'.results.nothing'};
        }

        $ret .= " p?><?p " . BML::ml('.results.message', { 'url' => '/userinfo.bml?user=' . $u->{'user'} });
        $ret .= " " . BML::ml('.results.goback',
                               { 'url' => '/userinfo.bml?user=' . LJ::eurl($POST{'fromuser'}),
                                 'user' => LJ::ljuser($POST{'fromuser'}),
                               }) if ($POST{'fromuser'} ne "" && $POST{'fromuser'} ne $u->{'user'});
        $ret .= " p?>";
        LJ::memcache_kill($u, "intids");
        return $ret;
    }

    if (!$did_post && ($GET{'intid'} || $GET{'int'})) {
        my $sth;
        my $dbr = LJ::get_db_reader();
        my ($interest, $intid, $intcount);
        if ($GET{'intid'}) {
            ($interest, $intid, $intcount) = $dbr->selectrow_array("SELECT interest, intid, intcount
                FROM interests WHERE intid=?", undef, $GET{'intid'});
        } else {
            ($interest, $intid, $intcount) = $dbr->selectrow_array("SELECT interest, intid, intcount
                FROM interests WHERE interest=?", undef, $GET{'int'});
        }
        return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.findsim_do.intnotfound'} p?>" unless $interest;
        $intid += 0;

        my $ret = "";

        ### hook
        LJ::run_hooks("interests_bml", {
            'intid' => $intid,
            'int' => $interest,
            'ret' => \$ret,
        });

        ### communities
        my $LIMIT = 500;

        unless ($LJ::DISABLED{'interests-community'}) {
            my @uids;
            $sth = $dbr->prepare("SELECT userid FROM comminterests WHERE intid=? LIMIT $LIMIT");
            $sth->execute($intid);
            push @uids, $_ while $_ = $sth->fetchrow_array;
            my $us = LJ::load_userids(@uids);
            my @cl = grep { $_->{statusvis} eq "V" } values %$us;
            @cl = sort { $a->{user} cmp $b->{user} } @cl;
            my $count = @cl;

            my $list;
            foreach (@cl) {
                my $name = $_->{name};
                LJ::text_out(\$name);
                $list .= "<li>" . LJ::ljuser($_) . " - " . LJ::ehtml($name) . "</li>";
            }

            if (@cl) {
                my $matchcount = BML::ml(@cl == 1 ? ".match" : ".matches", {'count' => $count });
                $ret .= "<?h1 $ML{'.communities.head'} h1?><?p " .
                    BML::ml(".communities.text", {'interest' => $interest}) ." p?>";
                $ret .= "<p><b>$matchcount</b><ul>$list</ul></p>";
            }
        }

        ##### users

        $ret .= "<?h1 $ML{'.users.head'} h1?><?p " . BML::ml(".users.text", {'interest' => $interest});
        if ($remote) {
            $ret .= " " . BML::ml(".addint", {'qintid' => $intid});
        }
        $ret .= " $ML{'.morestuff'} p?>";

        my @uids;
        $sth = $dbr->prepare("SELECT userid FROM userinterests WHERE intid=? LIMIT $LIMIT");
        $sth->execute($intid);
        push @uids, $_ while $_ = $sth->fetchrow_array;
        my $us = LJ::load_userids(@uids);
        my @ul = grep { $_->{statusvis} eq "V" && $_->{journaltype} ne "C" } values %$us;
        @ul = sort { $a->{user} cmp $b->{user} } @ul;
        my $count = @ul;

        my $list;
        foreach (@ul) {
            my $name = $_->{name};
            LJ::text_out(\$name);
            $list .= "<li>" . LJ::ljuser($_) . " - " . LJ::ehtml($name) . "</li>";
        }

        my $matchcount = BML::ml(@ul == 1 ? ".match" : ".matches", { 'count' => $count });
        $ret .= "<p><b>$matchcount</b><ul>$list</ul></p>";

        return $ret;
    }

    my $ret = "";
    $ret .= "<?h1 $ML{'.interests.head'} h1?><?p $ML{'.interests.text'} p?>";
    $ret .= "<table cellspacing='5' style='margin-top: 10px; margin-left: 30px; margin-bottom: 10px;'>";

    unless ($LJ::DISABLED{'interests-popular'}) {
        $ret .= "<tr valign='top'><td colspan='2'>";
        $ret .= "<a href=\"interests.bml?view=popular\">$ML{'.interests.viewpop'}</a></td></tr>";
    }

    $ret .= "<tr valign='top'><td align='left'>$ML{'.interested.in'}</td>";
    $ret .= "<td><form method='get' action='interests.bml'>";
    $ret .= LJ::html_text({name => 'int', size => 20}) . "&nbsp;";
    $ret .= LJ::html_submit(undef, $ML{'.interested.btn.find'});
    $ret .= "</form></td></tr>";

    if (!$LJ::DISABLED{'interests-findsim'} && $remote && LJ::get_cap($remote, "findsim")) {
        $ret .= "<tr valign='top'><td>$ML{'.interests.findsim'}</td><td><form method='get' action='interests.bml'>";
        $ret .= LJ::html_hidden('mode', 'findsim_do');
        $ret .= LJ::html_text({name => 'user', value => $remote->{'user'}, size => 20}) . "&nbsp;";
        $ret .= LJ::html_submit(undef, $ML{'.interested.btn.find'});
        $ret .= "</form></td></tr>";
    }

    $ret .= "<tr valign='top'><td>$ML{'.enmasse.intro'}</td>";
    $ret .= "<td><form method='get' action='interests.bml'>";
    $ret .= LJ::html_text({name => 'fromuser', size => 20}) . "&nbsp;";
    $ret .= LJ::html_submit(undef, $ML{'.enmasse.btn'});
    $ret .= LJ::html_hidden('mode', 'enmasse');
    $ret .= "</form></td></tr>";

    $ret .= "</table>";
    $ret .= $ML{'.nointerests.text'};

    return $ret;
}
_code?>

<=body
page?><?_c <LJDEP>
link: htdocs/interests.bml, htdocs/editinfo.bml
post: htdocs/interests.bml
form: htdocs/interests.bml
</LJDEP> _c?>
