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

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

module Qwik
  class FileSystemDB
    include Enumerable

    def initialize(path, spath)
      @path = path
      @path_h = {}
      @spath = spath
      @spath_h = {}
      @backupdb = BackupDB.new(@path)
    end
    attr_reader :backupdb

    def close
      # do nothing
    end

    def exist?(k)
      begin
	return true if baseexist?(k)
	return true if spath(k).exist?
      rescue
	return false
      end
      return false
    end

    def baseexist?(k)
      begin
	return true if path(k).exist?
      rescue
	return false
      end
      return false
    end

    def create(k)
      touch(k) unless exist?(k)
    end

    def get(k)
      if  path(k).exist?
	str = path(k).open{|f| f.read }
	return str
      end
      return spath(k).open{|f| f.read } if spath(k).exist?
      ""
    end

    def mtime(k)
      return  path(k).mtime if  path(k).exist?
      return spath(k).mtime if spath(k).exist?
      Time.at(0) # the file is already deleted.
    end

    def add(k, v)
      put(k, get(k)+v)
    end

    def put(k, v, time=Time.now)
      path(k).open("wb"){|f| f.print(v) }
      @backupdb.put(k, time, v)
    end
    alias set put

    def touch(k)
      add(k, "") # add null String
    end

    def delete(k)
      path(k).unlink if path(k).exist?
    end

    def each
      dir = @path.to_s
      Dir.foreach(dir) {|file|
	f = dir+"/"+file
	next unless FileTest.file?(f)
	base = file.to_s
	if /\A([_A-Za-z0-9]+)\.txt\z/ =~ base
	  b = $1
	  yield(b)
	end
      } 
    end

    def last_page_time # mtime of the newest page
      t = map {|k| mtime(k) }.max
      t = Time.at(0) if t.nil?
      t
    end

    # QuickML support
    def last_article_time # mtime of the newest file
      max = maxfile = nil
      @path.each_entry {|file|
	f = @path+file
	next unless f.file?
	base = file.to_s
	next if /\A\./ =~ base || base == ",config" || base == ",charset"
	mt = f.mtime
	if max.nil? || max < mt
	  max = mt 
	  maxfile = f
	end
      }
      max # return nil if there is no file.
    end

    private

    def path(k)
      gen_path(@path, @path_h, k)
    end

    def spath(k)
      gen_path(@spath, @spath_h, k)
    end

    def gen_path(path, h, k)
      pa = h[k]
      return pa if pa
      raise "Character type error "+k unless /\A([_A-Za-z0-9]+)\z/ =~ k
      h[k] = path+(k+".txt")
      h[k]
    end
  end
end

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

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

    def teardown
      @dir.teardown
    end

    def test_fsdb
      assert_equal(false, @pagedb.exist?("1"))

      @pagedb.create("1")
      assert_equal(true, @pagedb.exist?("1"))
      assert_equal("", @pagedb.get("1"))

      @pagedb.put("1", "t")
      assert_equal("t", @pagedb.get("1"))

      assert_instance_of(Time, @pagedb.mtime("1"))

      @pagedb.add("1", "s")
      assert_equal("ts", @pagedb.get("1"))

      assert_instance_of(Time, @pagedb.last_page_time)

      assert_instance_of(Time, @pagedb.last_article_time)

      @pagedb.each {|f|
	assert_instance_of(String, f)
      }

#      assert_instance_of(Qwik::BackupDB, @pagedb.backup_db("1"))

      @pagedb.touch("1")

      @pagedb.delete("1")
      assert_equal(false, @pagedb.exist?("1"))

      @pagedb.touch("1")
      assert_equal(true, @pagedb.exist?("1"))

      @pagedb.delete("1")
      assert_equal(false, @pagedb.exist?("1"))

      assert_equal(true,  @pagedb.exist?("_SideMenu"))
      assert_equal(false, @pagedb.baseexist?("_SideMenu"))

      # test_not_exist
      assert_equal(false, @pagedb.exist?("1"))

      @pagedb.put("1", "t")
      assert_equal(true, @pagedb.exist?("1"))
    end
  end
end
