# Copyright (C) 2003-2005 Kouichirou Eto, All rights reserved.

require "pathname"
$LOAD_PATH << "../../lib" unless $LOAD_PATH.include?("../../lib")
require "qwik/bdb"

module Qwik
  class BackupBDB
    def initialize(path)
      @backup_path = path+".backup"
      @db = open_db(path+"backup.db")
    end

    def open_db(path)
      options = 0
      unless path.exist? # don't over write
	options = BDB::CREATE | BDB::EXCL
      end
      db = BDB::Hash.open(path.to_s, nil, options)
      return db
    end

    def close
      @db.close
    end

    def path(k, time) # k is String, t is Time
      t = time.to_i.to_s
      @backup_path+(t+"_"+k)
    end
    private :path

    def exist?(k, time)
      v = db_get(k, time)
      return true if v
      path(k, time).exist?
    end

    def get(k, time)
      v = db_get(k, time)
      return v if v
      if  path(k, time).exist?
	str = path(k, time).open {|f| f.read }
	put(k, time, v)
	return str
      end
      ""
    end

    def db_get(k, time)
      @db.get(make_key(k, time))
    end

    def make_key(k, time)
      k+","+time.to_i.to_s
    end
    private :db_get, :make_key

    def put(k, time, v)
      @db.put(make_key(k, time), v)
    end
    alias set put

    def each_by_key(key)
      ar = []
      # FIXME: use each_by_prefix(key+",") instead
      @db.each {|k, v|
	next if v.nil?
	if /\A#{key},([0-9]+)\z/ =~ k # begin with key?
	  time = Time.at($1.to_i)
	  ar << [v, time]
	end
      }

      if @backup_path.exist?
	@backup_path.each_entry {|file|
	  f = @backup_path+file
	  next unless f.file?
	  base = file.to_s
	  if /\A([0-9]+)_#{key}\z/ =~ base # end with key ?
	    time = Time.at($1.to_i)
	    v = get(key, time)
	    ar << [v, time]
	  end
	}
      end

      ar.sort_by {|v, time|
	time
      }.each {|v, time|
	yield(v, time)
      }
    end
  end
end

if $0 == __FILE__
  require "test/unit"
  require "qwik/config"
  require "qwik/test-module-path"
  $debug = true
end

if $debug
  class TC_BackupBDB < Test::Unit::TestCase
    def setup
      @config = Qwik::Config.new(true)
      @config.db = "bdb"
      @dir = "./test/".path
      @dir.setup
      path = @dir
      spath = @config.super_dir.path
      @pagedb = Qwik::BerkeleyDB.new(path, spath)
    end

    def teardown
      @pagedb.close if @pagedb
      @dir.teardown
    end

    def test_backupdb
      @pagedb.create("1")
      @pagedb.put("1", "t", Time.at(1))

      budb = @pagedb.backupdb
      assert_instance_of(Qwik::BackupBDB, budb)

      budb.each_by_key("1") {|v, time|
	assert_instance_of(String, v)
	assert_instance_of(Time, time)
	s = budb.get("1", time)
	assert_instance_of(String, s)
	assert_equal(v, s)
      }

      #put
      #exist?
    end
  end
end
