/*
 * LOGICAL-PARADOX.ORG
 * Copyright (C)2005 satoshi akabane(akabane@logical-paradox.org)
 * $Id: DistributedRandomAccessFile.java,v 1.3 2005/09/17 17:35:49 rampil Exp $
 */
package org.logical_paradox.vsan.vfi;

import java.io.EOFException;
import java.io.IOException;
import java.rmi.RemoteException;

import org.logical_paradox.common.io.RandomAccessFileDelegator;
import org.logical_paradox.common.util.ByteStreamConverter;
import org.logical_paradox.vsan.dfs.DomainConnectionProxy;
import org.logical_paradox.vsan.dfs.DomainDataStream;
import org.logical_paradox.vsan.dfs.DomainFileSystem;
import org.logical_paradox.vsan.dfs.exception.NoSuchAreaException;
import org.logical_paradox.vsan.lookup.LookupFactory;

/**
 * 分散ランダムアクセスファイル
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.3 $
 */
public class DistributedRandomAccessFile implements RandomAccessFileDelegator {
	/** 接続先ドメインファイルシステム */
	private final String dfsuri;
	/** クライアントID */
	private final String clientId;
	/** ドメインファイルシステム */
	private DomainFileSystem dfs;
	/** 接続プロキシ */
	private DomainConnectionProxy proxy;

	/** 現在のシークポインタ */
	private long seekp = 0;

	/**
	 * コンストラクタ
	 * @param dfsuri_ 接続先ドメインファイルシステム
	 * @param clientId_ クライアントID
	 */
	public DistributedRandomAccessFile(String dfsuri_, String clientId_) throws RemoteException {
		dfsuri = dfsuri_;
		clientId = clientId_;

		// ドメイン接続
		open();
	}
	/**
	 * ドメインファイルシステムに接続する．
	 * @throws RemoteException 接続に失敗
	 */
	protected void open() throws RemoteException {
		dfs = LookupFactory.getLookup().lookupDomainFileSystem(dfsuri);
		proxy = dfs.getConnection(clientId);
	}
	/**
	 * ドメインファイルシステムとの接続をクローズする
	 * @throws IOException クローズ処理に失敗
	 */
	public void close() throws IOException {
		proxy.disconnect();
	}

	/**
	 * ドメイン全体の占有サイズを返す．
	 * @return サイズ
	 * @throws IOException サイズの取得に失敗
	 */
	public long length() throws IOException {
		return proxy.size();
	}

	/**
	 * シークポインタを移動する．
	 * @return シークポインタ
	 */
	public void seek(long pos) throws IOException {
		seekp = pos;
	}

	/**
	 * バイトデータを読み込む．
	 * 最大で配列で指定したサイズバイト分読み込んで格納する．
	 * 読み込んだ後、シークポインタが移動する．
	 * @param b バッファ
	 * @return 実際に読み込まれたデータ(単位:バイト)
	 * @throws IOException 読み込みに失敗した場合の例外
	 */
	public int read(byte[] b) throws IOException {
		try {
			DomainDataStream ds = proxy.read(seekp, b.length);

			// ドメインファイルシステムからの回答を戻り値として設定する
			System.arraycopy(ds.getStream(), 0, b, 0, ds.getSize());

			int rb = ds.getSize();
			seekp += rb;

			return rb;
		} catch(NoSuchAreaException e) {
			// レコード範囲外の場合、EOFExceptionに変換する
			throw new EOFException("レコード範囲外");
		}
	}

	/**
	 * ファイル全体を読み込む．
	 * そんなこと不可能なので、この操作は禁止する．<br>
	 * このメソッドを呼び出すと、例外なくUnsupportedOperationExceptionが発生する．
	 * @param b バッファ
	 * @throws IOException 入力例外
	 */
	public void readFully(byte[] b) throws IOException {
		throw new UnsupportedOperationException();
	}

	/**
	 * 4バイト読み込む(int)．
	 * 読み込んだ後、シークポインタが移動する．
	 * @return 読み込んだデータ
	 * @throws IOException 入力例外
	 */
	public int readInt() throws IOException {
		byte[] intbuf = new byte[4];
		int readcnt = read(intbuf);

		if(readcnt < intbuf.length) {
			throw new EOFException();
		}

		return ByteStreamConverter.getInt(intbuf);
	}

	/**
	 * 8バイト読み込む(long)．
	 * 読み込んだ後、シークポインタが移動する．
	 * @return 読み込んだデータ
	 * @throws IOException 入力例外
	 */
	public long readLong() throws IOException {
		byte[] longbuf = new byte[8];
		int readcnt = read(longbuf);

		if(readcnt < longbuf.length) {
			throw new EOFException();
		}

		return ByteStreamConverter.getLong(longbuf);
	}

	/**
	 * バイト列を書き込む．
	 * 書き込んだ後、シークポインタを移動する．
	 * @param b バイト列
	 * @throws IOException 出力例外
	 */
	public void write(byte[] b) throws IOException {
		proxy.write(seekp, b);
		seekp += b.length;
	}

	/**
	 * 1バイト書き込む．
	 * 書き込んだ後、シークポインタを移動する．
	 * @param b 書き込むデータ
	 * @throws IOException 出力例外
	 */
	public void write(int b) throws IOException {
		write(new byte[]{(byte)b});
	}

}
