package jp.cssj.driver.ctip.common;

import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;

/**
 * SocketChannelから各種データを取得します。
 * それぞれのメソッドは非ブロッキングI/Oに対して動作しますが、データの取得が完了するまでブロックします。
 * 
 * @author <a href="mailto:miyabe at gnn.co.jp">MIYABE Tatsuhiko </a>
 * @version $Id: TcpUtils.java 548 2011-02-23 03:30:09Z miyabe $
 */
public final class TcpUtils {
	private TcpUtils() {
		// unused
	}

	/**
	 * 1バイト整数を読み込みます。
	 * 
	 * @param channel
	 * @param destByte
	 * @return　読み込んだ値。
	 * @throws IOException
	 */
	public static byte readByte(ReadableByteChannel channel, ByteBuffer destByte)
			throws IOException {
		TcpUtils.readAll(channel, destByte);
		return destByte.get(0);
	}

	/**
	 * 2バイト整数を読み込みます。
	 * 
	 * @param channel
	 * @param destShort
	 * @return　読み込んだ値。
	 * @throws IOException
	 */
	public static short readShort(ReadableByteChannel channel,
			ByteBuffer destShort) throws IOException {
		TcpUtils.readAll(channel, destShort);
		return destShort.getShort(0);
	}

	/**
	 * 4バイト整数を読み込みます。
	 * 
	 * @param channel
	 * @param destInt
	 * @return　読み込んだ値。
	 * @throws IOException
	 */
	public static int readInt(ReadableByteChannel channel, ByteBuffer destInt)
			throws IOException {
		TcpUtils.readAll(channel, destInt);
		return destInt.getInt(0);
	}

	/**
	 * 8バイト整数を読み込みます。
	 * 
	 * @param channel
	 * @param destLong
	 * @return　読み込んだ値。
	 * @throws IOException
	 */
	public static long readLong(ReadableByteChannel channel, ByteBuffer destLong)
			throws IOException {
		TcpUtils.readAll(channel, destLong);
		return destLong.getLong(0);
	}

	/**
	 * 文字列を読み込みます。 文字列は2バイトの文字列長(バイト数)に続く文字列本体のバイト列で構成されます。
	 * バイト列は指定したエンコーディングで文字列に変換します。
	 * 
	 * @param channel
	 * @param destShort
	 * @param encoding
	 * @return　読み込んだ文字列。
	 * @throws IOException
	 */
	public static String readString(ReadableByteChannel channel,
			ByteBuffer destShort, String encoding) throws IOException {
		short len = TcpUtils.readShort(channel, destShort);
		if (len == 0) {
			return "";
		}
		byte[] buff = readBytes(channel, len);
		return new String(buff, encoding);
	}

	/**
	 * 指定された長さだけバイト列を読み込みます。
	 * 
	 * @param channel
	 * @param len
	 * @return　読み込んだデータ。
	 * @throws IOException
	 */
	public static byte[] readBytes(ReadableByteChannel channel, int len)
			throws IOException {
		byte[] buff = new byte[len];
		ByteBuffer dest = ByteBuffer.wrap(buff);
		TcpUtils.readAll(channel, dest);
		return buff;
	}

	/**
	 * バッファがいっぱいになるまでデータを読み込みます。
	 * 
	 * @param channel
	 * @param dest
	 * @throws IOException
	 */
	public static void readAll(ReadableByteChannel channel, ByteBuffer dest)
			throws IOException {
		dest.position(0);
		do {
			if (channel.read(dest) == -1) {
				throw new EOFException();
			}
		} while (dest.remaining() > 0);
	}

	/**
	 * バッファが空になるまでデータを書き込みます。
	 * 
	 * @param channel
	 * @param src
	 * @throws IOException
	 */
	public static void writeAll(WritableByteChannel channel, ByteBuffer src)
			throws IOException {
		src.position(0);
		do {
			channel.write(src);
		} while (src.remaining() > 0);
	}

	/**
	 * 文字列をバイト列に変換します。 null文字列は空文字列として変換します。
	 * 
	 * @param str
	 * @param encoding
	 * @return　変換後のバイト列。
	 * @throws IOException
	 */
	public static byte[] toBytes(String str, String encoding)
			throws IOException {
		if (str == null) {
			str = "";
		}
		return str.getBytes(encoding);
	}
}