#/*
# *  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: Storable.pm 1826 2009-04-08 15:23:25Z hikarin $
#

package Img0ch::Maple::TC;

use strict;
use File::Basename qw();
use File::Path qw();
use TokyoCabinet qw();

sub new {
    my ( $iClass, $iKernel, $path ) = @_;
    my $type = $iKernel->get_config()->get('TokyoCabinetStorageType')
        || 'HDB';
    my $class = 'TokyoCabinet::' . $type;
    bless {
        __class    => $class,
        __filename => $path,
        __kernel   => $iKernel
    }, $iClass;
}

sub DESTROY { }

sub _error {
    my ( $iKernel, $tc ) = @_;
    $iKernel->throw_exception( 'TokyoCabinet error at ',
        $tc->path(), ': ', $tc->errmsg( $tc->ecode() ) );
}

sub load {
    my ( $iRepos, $path ) = @_;
    my $iKernel = $iRepos->{__kernel};
    my $tc      = $iRepos->{__class}->new();
    $path ||= $iRepos->{__filename};
    $path or return 0;
    _init( $tc, $path );
    $tc->open( $path, $tc->OWRITER() | $tc->OCREAT() )
        or _error( $iKernel, $tc );
    $tc->close() or _error( $iKernel, $tc );
    return 1;
}

sub save {1}

sub set {
    my ( $iRepos, $key, $value ) = @_;
    my $iKernel = $iRepos->{__kernel};
    my $tc      = $iRepos->{__class}->new();
    my $path    = $iRepos->{__filename};
    _init( $tc, $path );
    $tc->open( $iRepos->{__filename}, $tc->OWRITER() )
        or _error( $iKernel, $tc );
    $tc->put( $key, $value ) or _error( $iKernel, $tc );
    $tc->close() or _error( $iKernel, $tc );
    return;
}

*set_binary = \&set;

sub get {
    my ( $iRepos, $key ) = @_;
    my $iKernel = $iRepos->{__kernel};
    my $tc      = $iRepos->{__class}->new();
    $tc->open( $iRepos->{__filename}, $tc->OREADER() )
        or _error( $iKernel, $tc );
    my $value = $tc->get($key) || '';
    $tc->close() or _error( $iKernel, $tc );
    return $value;
}

*get_binary = \&get;

sub decode_binary { shift; return $_[0] }

sub is_binary_safe {1}

sub get_int { Img0ch::Kernel::intval( $_[0]->get( $_[1] ) ) }

sub remove {
    my ( $iRepos, $key ) = @_;
    my $iKernel = $iRepos->{__kernel};
    my $tc      = $iRepos->{__class}->new();
    my $path    = $iRepos->{__filename};
    _init( $tc, $path );
    $tc->open( $path, $tc->OREADER() | $tc->OWRITER() )
        or _error( $iKernel, $tc );
    my $value = $tc->get($key);
    $tc->vsiz($key) != -1 and !$tc->out($key) and _error( $iKernel, $tc );
    $tc->close() or _error( $iKernel, $tc );
    return $value;
}

sub iterate {
    my ( $iRepos, $code_ref, @args ) = @_;
    my $iKernel = $iRepos->{__kernel};
    my $tc      = $iRepos->{__class}->new();
    my $path    = $iRepos->{__filename};
    _init( $tc, $path );
    $tc->open( $path, $tc->OREADER() | $tc->OWRITER() )
        or _error( $iKernel, $tc );
    $tc->iterinit();
    while ( my $key = $tc->iternext() ) {
        my $value = $tc->get($key) || '';
        my $status = $code_ref->( $key, \$value, @args );
        if ( $status > 0 ) {
            $tc->put( $key, $value ) or _error( $iKernel, $tc );
        }
        elsif ( $status < 0 ) {
            $tc->vsiz($key) != -1
                and !$tc->out($key)
                and _error( $iKernel, $tc );
        }
    }
    $tc->close() or _error( $iKernel, $tc );
    return 1;
}

*iterate_binary = \&iterate;

sub _init {
    my ( $tc, $path ) = @_;
    my $dir = File::Basename::dirname($path);
    -d $dir or File::Path::mkpath($dir);
    -e $path or $tc->tune( 1024, undef, undef, $tc->TTCBS() );
}

1;
__END__
