/**
 * LOGICAL-PARADOX.ORG
 * Copyright (C)2004 satoshi akabane(akabane@logical-paradox.org)
 *
 */
package org.logical_paradox.fts;

import java.io.IOException;

import org.logical_paradox.common.btree.Btree;
import org.logical_paradox.common.btree.BtreeException;
import org.logical_paradox.common.btree.BtreeResultSet;
import org.logical_paradox.common.btree.BtreeSession;
import org.logical_paradox.common.vrm.Record;
import org.logical_paradox.common.vrm.RecordFactory;
import org.logical_paradox.common.vrm.VariableRecordManager;

/**
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.3 $
 */
public class CommonBtreeIndexAccessor implements IndexAccessor {
	private final BtreeSession session;
	private static VariableRecordManager theVrm;
	private final VariableRecordManager vrm;

	/**
	 * デフォルトコンストラクタ
	 * @param filename ファイル名
	 */
	public CommonBtreeIndexAccessor(String filename) throws IOException {
		try {
			session = Btree.getInstance(new FTSBtreeFactory()).getSession().open(filename);
			// デフォルトのレコードファクトリで可変長レコードマネージャーを作成する
			vrm = connect(filename);
		} catch(BtreeException be) {
			be.printStackTrace();
			throw new IOException(be.getMessage());
		} catch(Exception e) {
			e.printStackTrace();
			throw new IOException(e.getMessage());
		}
	}
	private static VariableRecordManager connect(String filename) throws IOException, Exception {
		if(theVrm == null) {
			theVrm = new VariableRecordManager(filename + ".dat", RecordFactory.getInstance("org.logical_paradox.fts.KeywordLocationArrayRecordFactory"));
		}
		return theVrm;
	}
	/**
	 * アクセサがクローズされているかどうかを評価する
	 * @return true: 閉じている / false:開いている
	 */
	public boolean isClosed() {
		return session.isClosed();
	}
	/**
	 * インデックスアクセサを閉じる
	 * @exception IndexAccessException クローズ処理中に何らかの例外が発生した
	 */
	public void close() throws IndexAccessException {
		try {
			vrm.close();
			session.close();
		} catch(Exception be) {
			throw new IndexAccessException(be.getMessage());
		}
	}
	/**
	 * 新しいキーをストレージに挿入する
	 * @param k キー
	 * @param o オブジェクト
	 * @exception IndexAccessException 挿入処理中に何らかの例外が発生した
	 */
	public void addKey(Key k, Object o) throws IndexAccessException {
		try {
			// 指定されたキーに紐付けされているデータが既に登録されているかどうかを調べる
			// 既に存在する場合，元のデータは削除しなければならない
			FTSBtreeKey key = (FTSBtreeKey)session.newKey(k.toString());
			BtreeResultSet rs = session.find(key);
			if(rs != null) {
				FTSBtreeKey oldKey = (FTSBtreeKey)rs.getKey();
				if(oldKey != null) {
					// 既に登録済みの場合はデータを削除
					vrm.delete(key.getOffset());
					// ファイルから取得したキーを再利用
					key = oldKey;
				}
			}
			// VRMに対してまずデータを登録する
			// 登録するデータは必ずRecordの派生クラスでなければならない
			long offset = vrm.insert((Record)o);

			// バイト変位を設定してキーを登録する
			// 既に存在する場合はキーの更新になる
			key.setOffset(offset);
			session.insert(key);
		} catch(Exception be) {
			be.printStackTrace();
			throw new IndexAccessException(be.getMessage());
		}
	}
	/**
	 * キーに対応するオブジェクトを削除する
	 * @param k キー
	 * @return 削除されたオブジェクト
	 * @exception IndexAccessException 削除に失敗した
	 */
	public Object removeKey(Key k) throws IndexAccessException {
		// 削除はまだ実装していないはず
		return null;
	}

	/**
	 * キーに対応する値オブジェクトを探して返す
	 * @param k キー
	 * @return 検出されたオブジェクト
	 * @exception IndexAccessException 検索に失敗した
	 */
	public Object getValue(Key k) throws IndexAccessException {
		// キーそのものがオブジェクトであると仮定して，キーを返す
		try {
			BtreeResultSet result = session.find(session.newKey(k.toString()));
			if(result == null) {
				return null;
			}
			FTSBtreeKey key = (FTSBtreeKey)result.getKey();
			if(key == null) {
				return null;
			}

			// VRMから該当位置のデータを取得して返す
			return vrm.get(key.getOffset());
		} catch(BtreeException be) {
			throw new IndexAccessException(be.getMessage());
		} catch(IOException ioe) {
			ioe.printStackTrace();
			throw new IndexAccessException(ioe.getMessage());
		}
	}
}
