#/*
# *  Copyright 2007-2008 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: BDB.pm 1645 2008-02-06 15:40:18Z hikarin $
#

package Img0ch::Maple::BDB;

use strict;
use BerkeleyDB;

sub new {
    my ( $iClass, $iKernel, $path ) = @_;
    bless {
        __bdb      => undef,
        __bdb_env  => undef,
        __bdb_txn  => undef,
        __filename => $path,
        __kernel   => $iKernel
    }, $iClass;
}

sub DESTROY { }

sub load {
    my ( $iRepos, $path ) = @_;
    my $iKernel = $iRepos->{__kernel};

    my $env = BerkeleyDB::Env->new(
        -Home  => $iKernel->get_config()->get('BDBHome'),
        -Flags => DB_CREATE | DB_INIT_TXN | DB_INIT_MPOOL | DB_INIT_LOCK
    ) or $iKernel->throw_exception($BerkeleyDB::Error);
    my $txn = $env->txn_begin();
    my $db  = BerkeleyDB::Hash->new(
        -Env      => $env,
        -Filename => ( $path || $iRepos->{__filename} ),
        -Flags    => DB_CREATE,
        -Txn      => $txn
    ) or $iKernel->throw_exception($BerkeleyDB::Error);
    $txn->txn_commit() == 0
        or $iKernel->throw_exception($BerkeleyDB::Error);
    $db->filter_fetch_key( sub   {s/\0\z//xms} );
    $db->filter_store_key( sub   { $_ .= "\0" } );
    $db->filter_fetch_value( sub {s/\0\z//xms} );
    $db->filter_store_value( sub { $_ .= "\0" } );
    my $new_txn = $env->txn_begin();
    $db->Txn($new_txn);

    $iRepos->{__bdb}     = $db;
    $iRepos->{__bdb_env} = $env;
    $iRepos->{__bdb_txn} = $new_txn;
    return 1;
}

sub save {
    my ( $iRepos, $path ) = @_;
    my $iKernel = $iRepos->{__kernel};
    $iRepos->{__bdb_txn}->txn_commit() == 0
        or $iKernel->throw_exception($BerkeleyDB::Error);
    my $new_txn = $iRepos->{__bdb_env}->txn_begin();
    $iRepos->{__bdb}->Txn($new_txn);
    $iRepos->{__bdb_txn} = $new_txn;
    return 1;
}

sub set {
    my ( $iRepos, $key, $value ) = @_;
    $iRepos->{__bdb}->db_put( $key, $value ) == 0
        or $iRepos->{__kernel}->throw_exception($BerkeleyDB::Error);
    return;
}

*set_binary = \&set;

sub get {
    my ( $iRepos, $key ) = @_;
    my $value;
    $iRepos->{__bdb}->db_get( $key, $value );
    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 $value = $iRepos->get($key);
    $iRepos->{__bdb}->db_del($key);
    return $value;
}

sub iterate {
    my ( $iRepos, $code_ref, @args ) = @_;
    my ( $key, $value );
    my $cursor = $iRepos->{__bdb}->db_cursor();

    while ( $cursor->c_get( $key, $value, DB_NEXT ) == 0 ) {
        my $status = $code_ref->( $key, \$value, @args );
        if ( $status > 0 ) {
            $cursor->c_put( $key, $value, DB_CURRENT );
        }
        elsif ( $status < 0 ) {
            $cursor->c_del();
        }
    }
    return 1;
}

1;
__END__
