#/*
# *  Copyright 2007-2010 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 1933 2010-01-21 00:53:09Z 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 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} ) {

            # my $iLog   = $iBBS->get_log_instance($key);
            my $plugin = $zPlugin->iterator( 'zeromin.erase.res', sub { } );
            my $line = $zThread->{_line};

            my @temp_range = @$range;
            for my $resno (@temp_range) {
                $resno = Img0ch::Kernel::intval($resno);
                $resno < 2 and next;
                splice @$line, ( $resno - 1 ), 1;
                $plugin->($resno);
                map { $_ = $_ - 1 } @temp_range;    ## no critic
            }

            $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::Thread::Revision;
            my $iSetting = $option->{setting}
                || $iBBS->get_setting_instance();
            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 move {
    my ( $zThread, $toBBS, $delete ) = @_;
    my $iKernel     = $zThread->{_kernel};
    my $fromBBS     = $zThread->get_bbs();
    my $key         = $zThread->get_key();
    my $fromDatPath = $zThread->path();
    my $toDatPath   = $toBBS->path( 'dat', $key . '.dat' );
    my $fromImgPath = $fromBBS->path( 'img', $key );
    my $toImgPath   = $toBBS->path( 'img', $key );

    -e $toDatPath and return 0;
    if ( -d $fromImgPath ) {
        symlink( $fromImgPath, $toImgPath )
            or $iKernel->throw_io_exception($toImgPath);
    }

    defined $File::Copy::VERSION or require File::Copy;
    my $fromReposPath
        = Img0ch::Maple::create_repository_subdirs( $key, '', $fromBBS );
    my $toReposPath
        = Img0ch::Maple::create_repository_subdirs( $key, '', $toBBS );
    $fromReposPath =~ s{/\.\w*\z}{/}gxms;
    $toReposPath   =~ s{/\.\w*\z}{/}gxms;
    opendir my $fh, $fromReposPath
        or $iKernel->throw_io_exception($fromReposPath);
    my @files = readdir($fh);
    closedir $fh or $iKernel->throw_io_exception($fromReposPath);

    foreach my $file (@files) {
        ( $file eq '.' or $file eq '..' ) and next;
        my $fromFile = $fromReposPath . '/' . $file;
        my $toFile   = $toReposPath . '/' . $file;
        File::Copy::move( $fromFile, $toFile )
            or $iKernel->throw_io_exception($toFile);
    }
    File::Copy::copy( $fromDatPath, $toDatPath )
        or $iKernel->throw_io_exception($toDatPath);

    @{ $zThread->{_line} } = (0);
    my $charset = $fromBBS->get_encoding();
    my $title
        = $iKernel->get_encoded_str( $iKernel->translate_symbol('MovedTitle'),
        $charset, 'utf8' );
    my $move = $iKernel->get_encoded_str( $iKernel->translate_symbol('Move'),
        $charset, 'utf8' );
    my $message
        = $iKernel->get_encoded_str(
        $iKernel->translate_symbol('MovedMessage'),
        $charset, 'utf8' );
    my $url = $toBBS->url('');
    $message =~ s/{BR}/<br>/gxms;
    $message =~ s/{URL}/$url/gxms;

    if ($delete) {
        unlink $fromDatPath or $iKernel->throw_io_exception($fromDatPath);
    }
    else {
        $zThread->set( [ $title, $move, $move, '', $message, $title ], 1 );
        $zThread->{_count} = 1;
        $zThread->save();
    }
    $zThread->_plugin('zeromin.move.thread');

    return 1;
}

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

    my $charset = $zThread->get_bbs()->get_encoding();
    my $title
        = $iKernel->get_encoded_str(
        $iKernel->translate_symbol('StoppedTitle'),
        $charset, 'utf8' );
    my $stop = $iKernel->get_encoded_str( $iKernel->translate_symbol('Stop'),
        $charset, 'utf8' );
    my $message
        = $iKernel->get_encoded_str(
        $iKernel->translate_symbol('StoppedMessage'),
        $charset, 'utf8' );
    $zThread->set( [ $title, $stop, $stop, '', $message ] );
    $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];
    my $subject = $set->[5];

    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, $subject ], $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( $zThread->count() );
    $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;

    defined $File::Copy::VERSION or 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 )
    {
        defined $Archive::Tar::VERSION or require Archive::Tar;
        defined $File::Temp::VERSION   or 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 (
        $detect_hint =~ /\.zip/xms
        and ( defined $Compress::Zlib::VERSION
            or eval 'use Compress::Zlib qw(); 1' )
        )
    {
        defined $Archive::Zip::VERSION or 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 {
        my $rfh = $iKernel->get_read_file_handle($dest);
        my $wfh = $iKernel->get_write_file_handle($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__
