/**
 * LOGICAL-PARADOX.ORG variable record manager library
 * Copyright (C)2004 satoshi akabane(akabane@logical-paradox.org)
 *
 * Release 1.0
 * ・初期パージョン
 */
package org.logical_paradox.common.vrm;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Iterator;

import org.logical_paradox.common.io.AvailableList;
import org.logical_paradox.common.util.Cache;
import org.logical_paradox.common.util.PriorityCache;

/**
 * VRMで使用するAvailableList
 * 先頭の8バイトは，利用可能レコードリストの最初のレコードに対する
 * バイト変位．<BR>
 * 以降，レコードサイズ,次のレコードのバイト変位の順になる．<br>
 * 利用可能なレコードは大きい順でソートされて格納されているはず．
 * 
 * @author satoshi akabane@logical-paradox.org
 * @version 1.0
 */
public class VRMAvailableList extends AvailableList {
	private long offset;

	/**
	 * コンストラクタ
	 * キャッシュサイズは無制限
	 * @param filename ファイル名
	 */
	public VRMAvailableList(String filename) {
		super(filename);
	}
	/**
	 * コンストラクタ
	 * @param filename ファイル名
	 * @param size キャッシュサイズ(0:無制限)
	 */
	public VRMAvailableList(String filename, int size) {
		super(filename, size);
	}
	/**
	 * この利用可能リストで使用するキャッシュオブジェクトを生成する
	 * @param size
	 * @return
	 */
	protected Cache createCache(int size) {
		// worst-fitを実現するために，レコードサイズの大きい順にソートされるキャッシュを作成する
//		return new PriorityCache(size, PriorityCache.GREATER_FIRST);
		return new Cache(size);
	}
	/**
	 * バイト列から利用可能リストを復元する
	 * @param buffer 入力元
	 * @throws IOException ファイルアクセス中に何か問題が発生した
	 */
	protected void restore(ByteBuffer buffer) throws IOException {
		new RestoreThread(buffer, cache).start();
	}
	/**
	 * 最初の利用可能レコードのバイト変位を設定する
	 * @param o バイト変位
	 */
	public void setOffset(long o) {
		offset = o;
	}
	/**
	 * 最初の利用可能レコードのバイト変位を返す
	 * @return バイト変位
	 */
	public long getOffset() {
		return offset;
	}
	/**
	 * 利用可能リストをファイルへ出力する
	 * @throws IOException ファイル出力中に何らかの問題が発生した
	 */
	public void flush() throws IOException {
		OutputStream out = getOutputStream();
		
		// 利用可能レコードの最初のバイトオーダーを出力
		ByteBuffer buffer = ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN);
		buffer.putLong(offset);
		buffer.rewind();
		
		out.write(buffer.array());

		// レコードbodyの出力
		AvailableRecord ar = null;
		for(Iterator it = cache.iterator(); it.hasNext();) {
			ar = (AvailableRecord)it.next();
			out.write(ar.byteStream());
		}
		
		// ファイルを閉じて終了
		out.flush();
		out.close();
	}

	/**
	 * 利用可能リストを復元するスレッド
	 * @author satoshi akabane@logical-paradox.org
	 */
	class RestoreThread extends Thread {
		private ByteBuffer buffer;
		private Cache cache;

		/**
		 * コンストラクタ
		 * @param buf 読み込んだバイト列
		 * @param cache 出力先のキャッシュクラス
		 */
		RestoreThread(ByteBuffer buf, Cache cache) {
			this.buffer = buf;
			this.cache = cache;
		}
		/**
		 * スレッドメイン
		 */
		public void run() {
			// 先頭レコードのバイト変位を取得する
			if(buffer.hasRemaining()) {
				offset = buffer.getLong();
			} else {
				offset = 0;
			}
			while(buffer.hasRemaining()) {
				// 利用可能レコードのサイズ取得
				int availableRecordSize = buffer.getInt();
				// 次のレコードへのポインタ取得
				long nextptr = buffer.getLong();
				// レコードを復元してキャッシュに追加する
				cache.add(new AvailableRecord(availableRecordSize, nextptr));
				if(nextptr < 0) {
					// 最後のレコードまで取り込んだので終了
					break;
				}
			}

			System.out.println("利用可能リストの復元が完了しました");
		}
	}
}
