#/*
# *  Copyright 2007 hkrn <hikarin@users.sourceforge.jp>
# *
# *  Licensed under the Apache License, Version 2.0 (the "License");
# *  you may not use this file except in compliance with the License.
# *  You may obtain a copy of the License at
# *
# *      http://www.apache.org/licenses/LICENSE-2.0
# *
# *  Unless required by applicable law or agreed to in writing, software
# *  distributed under the License is distributed on an "AS IS" BASIS,
# *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# *  See the License for the specific language governing permissions and
# *  limitations under the License.
# */
#
# $Id: Log.pm 1131 2007-09-14 14:47:14Z hikarin $
#

package Img0ch::Log;

use strict;

sub new {
    my ( $iClass, $iBBS, $key ) = @_;
    my $iKernel = $iBBS->get_kernel();
    my $repos_path
        = Img0ch::Maple::create_repository_subdirs( $key, 'log', $iBBS );

    bless {
        _bbs    => $iBBS->get_name(),
        _i0_bbs => $iBBS,
        _key    => $key,
        __log   => [],
        _rs     => $iKernel->get_repos_nocache($repos_path),
    }, $iClass;
}

sub load { $_[0]->{_rs}->load(); return 1 }

sub save {
    my ($iLog) = @_;
    my $logs   = $iLog->{__log};
    my $iRepos = $iLog->{_rs};
    my ( $bbs, $key ) = ( $iLog->{_bbs}, $iLog->{_key} );
    my $prefix_top = "I:L.${bbs}.${key}";

    for my $log (@$logs) {
        my $resno   = $log->[0];
        my $content = $log->[1];
        my $prefix  = join '.', $prefix_top, $resno;
        my ( $date, $ip, $serial, $agent )
            = ( $content->[0], $content->[1], $content->[2], $content->[3] );
        $ip = join '.', unpack( 'C*', $ip );
        $iRepos->set( "${prefix}.date",   $date );
        $iRepos->set( "${prefix}.ip",     $ip );
        $iRepos->set( "${prefix}.serial", $serial );
        $iRepos->set_binary( "${prefix}.agent", substr( $agent, 0, 255 ) );
    }

    @{ $iLog->{__log} } = ();
    $iRepos->save();
    1;
}

sub get {
    my ( $iLog, $resno ) = @_;
    my $iRepos = $iLog->{_rs};
    my ( $bbs, $key ) = ( $iLog->{_bbs}, $iLog->{_key} );

    $resno = Img0ch::Kernel::intval($resno);
    my $prefix = "I:L.${bbs}.${key}.${resno}";
    my $date   = $iRepos->get("${prefix}.date");
    if ($date) {
        my $ip     = $iRepos->get("${prefix}.ip");
        my $serial = $iRepos->get("${prefix}.serial");
        my $agent  = $iRepos->get_binary("${prefix}.agent");
        $ip = pack 'C*', split( '\.', $ip );
        return [
            $date,   $ip,
            $serial, Img0ch::Kernel::escape_html_entities($agent),
        ];
    }

    return [ 0, '', '', '' ];
}

sub set {
    my ( $iLog, $resno, $log ) = @_;
    my $iRepos = $iLog->{_rs};
    my ( $bbs, $key ) = ( $iLog->{_bbs}, $iLog->{_key} );

    $resno = Img0ch::Kernel::intval($resno);
    my $prefix = "I:L.${bbs}.${key}.${resno}";
    my $ip = join '.', unpack( 'C*', $log->[1] );
    $iRepos->set( "${prefix}.date",   $log->[0] );
    $iRepos->set( "${prefix}.ip",     $ip );
    $iRepos->set( "${prefix}.serial", $log->[2] );
    $iRepos->set_binary( "${prefix}.agent", $log->[3] );
    return;
}

sub add {
    my ( $iLog, $resno, $log ) = @_;
    push @{ $iLog->{__log} }, [ $resno, $log ];
    return;
}

sub remove {
    my ( $iLog, $resno ) = @_;
    my $iRepos = $iLog->{_rs};
    my ( $bbs, $key ) = ( $iLog->{_bbs}, $iLog->{_key} );

    $resno = Img0ch::Kernel::intval($resno);
    my $prefix = "I:L.${bbs}.${key}.${resno}";
    my $date   = $iRepos->remove("${prefix}.date");
    if ($date) {
        $iRepos->remove("${prefix}.ip");
        $iRepos->remove("${prefix}.serial");
        $iRepos->remove("${prefix}.agent");
        return 1;
    }

    return 0;
}

sub search {
    my ( $iLog, $ip_or_serial ) = @_;
    $ip_or_serial or return [];

    require Img0ch::Subject;
    my $iBBS     = $iLog->{_i0_bbs};
    my $iSubject = Img0ch::Subject->new($iBBS);
    my $ret      = [];
    $iSubject->load();

    for my $key ( @{ $iSubject->to_array() } ) {
        my ( $subject, $res ) = @{ $iSubject->get($key) };
        my $iLog = Img0ch::Log->new( $iBBS, $key );
        for ( my $i = 1; $i <= $res; $i++ ) {
            my ( $date, $ip, $serial, $agent ) = @{ $iLog->get($i) };
            $date or next;
            $ip = join '.', unpack( 'C*', $ip );
            if ( $ip eq $ip_or_serial ) {
                push @$ret, [ $key, $i, $date, $ip, '', $agent, $subject ];
            }
            elsif ( $serial eq $ip_or_serial ) {
                push @$ret,
                    [ $key, $i, $date, '', $serial, $agent, $subject ];
            }
        }
    }

    return $ret;
}

1;
__END__

=head1 NAME

Img0ch::Log - 書き込み記録を管理するクラス

=head1 SYNOPSYS

  use Img0ch::Log

  my $iLog = Img0ch::Log->new( $iBBS, $key );
  $iLog->add( $resno, [ $date, $ip, $serial, $agent ] );
  $iLog->save();

  my ( $date, $ip, $serial, $agent ) = @{ $iLog->get($resno) };
  $iLog->remove($resno);
  $iLog->save();

=head1 DESCRIPTION

1つのスレッドの書き込み記録を1つのオブジェクトとするクラスです。

=head2 new

=over 4

=item Arguments

$iBBS (Img0ch::BBS), $key

=item Return Value

$iLog (Img0ch::Log itself)

=back

I<Img0ch::Log>のオブジェクトを作成します。

=head2 load

=over 4

=item Arguments

none

=item Return Value

1

=back

レポジトリを読み込みます。

=head2 save

=over 4

=item Arguments

none

=item Return Value

1

=back

add()で設定された内容をレポジトリに保存します。

=head2 get

=over 4

=item Arguments

$resno

=item Return Value

[ $date, $ip, $serial, $agent ]

=back

レス番号からログ情報の配列のリファレンスを返します。
$agentはHTML実体参照が行われた文字列で返します。

存在しない場合は$dateを0、それ以外の変数を
ナルストリングにした配列のリファレンスを返します。

=head2 set

=over 4

=item Arguments

$resno, [ $date, $ip, $serial, $agent ]

=item Return Value

none

=back

レス番号からログ情報を書き換えます。
実際に保存するにはsave()を呼び出す必要があります。

=head2 add

=over 4

=item Arguments

[ $date, $ip, $serial, $agent ]

=item Return Value

none

=back

ログ情報を追記します。実際に保存するにはsave()を呼び出す必要があります。

=head2 remove

=over 4

=item Arguments

$resno

=item Return Value

1(removed) or 0

=back

レス番号からログ情報を削除します。
ログ情報が存在しないレス番号が指定された場合0を返します。
実際に保存するにはsave()を呼び出す必要があります。

=head1 AUTHOR

hkrn E<lt>hikarin@users.sourceforge.jpE<gt>

=cut
