#/*
# *  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: Thread.pm 1103 2007-09-10 14:32:43Z hikarin $
#

package Zeromin::Thread;

use strict;
use base qw(Img0ch::Thread);

sub get_bbs { Img0ch::BBS->new( $_[0]->{_kernel}, { id => $_[0]->{_bbs} } ) }

sub set {
    my ( $zThread, $element, $pos ) = @_;
    if ( $pos = Img0ch::Kernel::intval($pos) ) {
        my $subject = (
            $pos == 1 ? ( $element->[5] || $zThread->get_subject() ) : '' );
        my $id = $element->[3] ? " $element->[3]" : '';
        my $line = "$element->[0]<>$element->[1]<>$element->[2]"
            . "$id<>$element->[4]<>${subject}\n";
        splice @{ $zThread->{_line} }, ( $pos - 1 ), 1, ($line);
    }
    else {
        $zThread->SUPER::set($element);
    }
    return;
}

sub remove {
    my ( $zThread, $range, $option ) = @_;
    my $iKernel = $zThread->{_kernel};
    my $bbs     = $zThread->{_bbs};
    my $key     = $zThread->{_key};

    require Img0ch::BBS;
    require Zeromin::Plugin;
    require Zeromin::Upload;
    my $iBBS = Img0ch::BBS->new( $iKernel, { id => $bbs } );
    my $zPlugin = Zeromin::Plugin->new( $iBBS, $key );
    my $zUpload = Zeromin::Upload->new( $iBBS, $key );

    if ($range) {
        $option ||= { is_erase => 0, is_file_only => 0 };

        my $count = $zThread->count();
        my $errno = 0;
        my @tmp   = ();

        $zThread->can_write( \$errno );
        for my $resno (@$range) {
            $resno < 2 and next;
            ( $resno >= $count and ( $errno >= 1 and $errno <= 3 ) ) and next;
            push @tmp, $resno;
        }
        @$range = @tmp;

        if ( $option->{is_erase} ) {
            require Img0ch::Log;
            my $iLog = Img0ch::Log->new( $iBBS, $key );
            my $plugin = $zPlugin->iterator( 'zeromin.erase.res', sub { } );
            my $line = $zThread->{_line};

            for my $resno ( @{$range} ) {
                $resno = Img0ch::Kernel::intval($resno);
                $resno < 2 and next;
                splice @$line, ( $resno - 1 ), 1;
                $plugin->($resno);
                $iLog->remove($resno);
            }
            $zUpload->remove($range);
            $zThread->save();
            @{ $zThread->{_line} } = @{$line};
            $zThread->{_count} -= scalar @{$range};
            $iLog->save();
        }
        elsif ( $option->{is_file_only} ) {
            my $plugin = $zPlugin->iterator( 'zeromin.remove.file', sub { } );
            for my $resno ( @{$range} ) {
                $resno = Img0ch::Kernel::intval($resno) || next;
                $zUpload->remove($resno);
                $plugin->($resno);
            }
        }
        else {
            require Img0ch::Setting;
            require Img0ch::Thread::Revision;
            my $iSetting   = Img0ch::Setting->new($iBBS);
            my $iTRevision = Img0ch::Thread::Revision->new( $iBBS, $key );
            my $word       = $iSetting->get('BBS_DELETE_NAME')
                || pack( 'C*',
                ( 0x82, 0xa0, 0x82, 0xda, 0x81, 0x5b, 0x82, 0xf1 ) );
            my $plugin = $zPlugin->iterator( 'zeromin.replace.res', sub { } );
            for my $resno ( @{$range} ) {
                $resno = Img0ch::Kernel::intval($resno) || next;
                $iTRevision->add( $resno, $zThread->get($resno) );
                $zThread->set( [ $word, $word, $word, '', $word ], $resno );
                $plugin->($resno);
            }
            $zUpload->remove($range);
            $iTRevision->save();
            $zThread->save();
        }
    }
    else {
        my $path = $zThread->path();
        if ( -w $path ) {
            unlink $path or $iKernel->throw_io_exception($path);
        }
        require Zeromin::Subject;
        my $zSubject = Zeromin::Subject->new($iBBS);
        $zSubject->load();
        $zSubject->remove($key);
        $zSubject->include_special_threads();
        $zSubject->save();
        $zUpload->remove();
        $zPlugin->do( 'zeromin.remove.thread', $iBBS, [$key] );
        @{ $zThread->{_line} } = ();
        $zThread->{_count} = 0;
    }

    return 1;
}

sub revert {
    my ( $zThread, $range_and_rev ) = @_;
    my $bbs = $zThread->{_bbs};
    my $key = $zThread->{_key};

    require Img0ch::Thread::Revision;
    my $iTRevision = Img0ch::Thread::Revision->new(
        Img0ch::BBS->new( $zThread->{_kernel}, { id => $bbs } ), $key );

    while ( my ( $resno, $revision ) = each %$range_and_rev ) {
        $resno = Img0ch::Kernel::intval($resno) || next;
        $revision = Img0ch::Kernel::intval($revision);
        my $content = $iTRevision->get( $resno, $revision );
        $content->[4] or next;
        $zThread->set( $content, $resno );
    }
    $zThread->save();

    return 1;
}

sub stop {
    my ($zThread) = @_;
    my $errno = 0;
    $zThread->can_write( \$errno ) or return 0;

    my $stop = pack( 'C*', ( 0x92, 0xe2, 0x8e, 0x7e ) );
    $zThread->set(
        [   pack(
                'C*',
                (   0x92, 0xe2, 0x8e, 0x7e, 0x82, 0xb5,
                    0x82, 0xdc, 0x82, 0xb5, 0x82, 0xbd,
                )
            ),
            $stop, $stop, '',
            pack(
                'C*',
                (   0x90, 0x5e, 0x81, 0x45, 0x83, 0x58, 0x83, 0x8c,
                    0x83, 0x62, 0x83, 0x68, 0x83, 0x58, 0x83, 0x67,
                    0x83, 0x62, 0x83, 0x70, 0x81, 0x5b, 0x81, 0x42,
                    0x81, 0x42, 0x81, 0x42, 0x81, 0x69, 0x81, 0x50,
                    0x81, 0x5b, 0x81, 0x50, 0x81, 0x6a, 0x83, 0x6a,
                    0x83, 0x84, 0x83, 0x8a, 0x83, 0x62,
                )
            ),
        ]
    );
    $zThread->save();
    $zThread->_plugin('zeromin.stop.thread');

    return 1;
}

sub restart {
    my ($zThread) = @_;
    my $errno = 0;

    $zThread->can_write( \$errno ) and return 0;
    $errno == 1 or return 0;
    splice @{ $zThread->{_line} }, ( $zThread->count() - 1 ), 1;
    $zThread->{_count}--;
    $zThread->save();
    $zThread->_plugin('zeromin.restart.thread');

    return 1;
}

sub edit {
    my ( $zThread, $resno, $set ) = @_;
    $resno = Img0ch::Kernel::intval($resno) || 1;
    $resno > $zThread->count() and return 0;

    my $default = $zThread->get($resno);
    my $from    = $set->[0];
    defined $from or $from = $default->[0];
    my $mail = $set->[1];
    defined $mail or $mail = $default->[1];
    my $date = $set->[2];
    defined $date or $date = $default->[2];
    my $id = $set->[3];
    defined $id or $id = $default->[3];
    my $comment = $set->[4];
    defined $comment or $comment = $default->[4];

    require Img0ch::Thread::Revision;
    my $iTRevision
        = Img0ch::Thread::Revision->new(
        Img0ch::BBS->new( $zThread->{_kernel}, { id => $zThread->{_bbs} } ),
        $zThread->{_key} );
    $iTRevision->add( $resno, $default );
    $iTRevision->save();
    $zThread->set( [ $from, $mail, $date, $id, $comment ], $resno );

    return 1;
}

sub repair {
    my ( $zThread, $info ) = @_;
    defined $info or $info = [];
    $info->[0] = $zThread->count();
    return;
}

sub repair_upload_file_table {
    my ( $zThread, $plugin_iterator ) = @_;
    my $zUpload = $zThread->{__upload};

    if ( !$zUpload ) {
        require Zeromin::Upload;
        my $iBBS = Img0ch::BBS->new( $zThread->{_kernel},
            { id => $zThread->{_bbs} } );
        $zThread->{__upload} = $zUpload = Zeromin::Upload->new( $iBBS, 0 );
    }

    $plugin_iterator ||= $zThread->{__plugin};
    if ( !$plugin_iterator ) {
        require Zeromin::Plugin;
        my $iBBS = Img0ch::BBS->new( $zThread->{_kernel},
            { id => $zThread->{_bbs} } );
        my $zPlugin = Zeromin::Plugin->new($iBBS);
        $plugin_iterator
            = $zPlugin->iterator( 'zeromin.repair.upload', sub { } );
        $zThread->{__plugin} = $plugin_iterator;
    }

    my $key = $zThread->{_key};
    $zUpload->set_key($key);
    $zUpload->repair();
    $plugin_iterator->($key);
    return 1;
}

sub import_from {
    my ( $zThread, $path, $detect_hint ) = @_;
    my $iKernel = $zThread->{_kernel};
    my $iBBS = Img0ch::BBS->new( $iKernel, { id => $zThread->{_bbs} } );
    $detect_hint ||= $path;

    require File::Copy;
    if ( $detect_hint =~ /\d{9,10}\.dat\z/xms ) {
        my $file = File::Basename::basename($detect_hint);
        my $dest = $iBBS->path( 'dat', $file );
        File::Copy::copy( $path, $dest )
            or $iKernel->throw_io_exception($path);
        _strip_tags_from_dat( $iKernel, $dest );
        return 1;
    }
    elsif ($detect_hint =~ /\.tar\.gz\z/xms
        or $detect_hint =~ /\.tgz\z/xms )
    {
        require Archive::Tar;
        require File::Temp;
        my $tar = Archive::Tar->new( $path, 1 )
            or $iKernel->throw_exception( Archive::Tar->error() );
        my $cwd = File::Temp::tempdir( CLEANUP => 1 );
        $tar->setcwd($cwd);
        my @list
            = $tar->extract( grep { $_->full_path() =~ /\d{9,10}\.dat\z/xms }
                $tar->get_files() );
        for my $member (@list) {
            my $dat    = join '/', $cwd, $member->full_path();
            my $suffix = File::Basename::basename($dat);
            my $dest   = $iBBS->path( 'dat', $suffix );
            File::Copy::copy( $dat, $dest )
                or $iKernel->throw_io_exception($dat);
            _strip_tags_from_dat( $iKernel, $dest );
        }
        return 1;
    }
    elsif ( eval "require Compress::Zlib; 1"
        and $detect_hint =~ /\.zip/xms )
    {
        require Archive::Zip;
        Archive::Zip::setErrorHandler(
            sub {
                my $error = shift;
                $iKernel->throw_exception($error);
            }
        );
        my $zip     = Archive::Zip->new($path);
        my @members = $zip->membersMatching('\d{9,10}\.dat');
        for my $member (@members) {
            my ( undef, $temp ) = Archive::Zip::tempFile();
            $member->extractToFileNamed($temp) == Archive::Zip::AZ_OK()
                or $iKernel->throw_exception($!);
            my $suffix = File::Basename::basename( $member->fileName() );
            my $dest = $iBBS->path( 'dat', $suffix );
            File::Copy::copy( $temp, $dest )
                or $iKernel->throw_io_exception($temp);
            _strip_tags_from_dat( $iKernel, $dest );
        }
        return 1;
    }
    else {
        return 0;
    }
}

sub export_to {

}

sub _plugin {
    my ( $zThread, $at ) = @_;
    require Zeromin::Plugin;
    my $iKernel = $zThread->{_kernel};
    my $bbs     = $zThread->{_bbs};
    my $key     = $zThread->{_key};
    my $iBBS    = Img0ch::BBS->new( $iKernel, { id => $bbs } );
    my $zPlugin = Zeromin::Plugin->new($iBBS);
    $zPlugin->do( $at, $zThread, [ $iBBS, $key ] );
    return;
}

sub _strip_tags_from_dat {
    my ( $iKernel, $dest ) = @_;
    my $temp = "${dest}.tmp";
    eval {
        local ( *RFH, *WFH, $! );
        open *RFH, "<${dest}"    ## no critic
            or $iKernel->throw_io_exception($dest);
        open *WFH, ">${temp}"    ## no critic
            or $iKernel->throw_io_exception($temp);
        while ( my $line = <RFH> ) {
            $line =~ s{</?a[^>]*>}{}gxms;
            print {*WFH} $line or $iKernel->throw_io_exception($temp);
        }
        close *RFH or $iKernel->throw_io_exception($dest);
        close *WFH or $iKernel->throw_io_exception($temp);
        rename $temp, $dest or $iKernel->throw_io_exception($dest);
    };
    if ( my $exception = $@ ) {
        if ( -e $temp ) {
            unlink $temp or $iKernel->throw_io_exception($temp);
        }
        die $exception, "\n";
    }
    return;
}

1;
__END__
