package org.phosphoresce.commons.util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringWriter;

/**
 * ストリーム操作におけるユーティリティクラス。<br>
 *
 * @author Kitagawa<br>
 *
 *<!--
 * 更新日		更新者			更新内容
 * 2008/11/05	Kitagawa		新規作成
 * 2010/07/19	Kitagawa		全てのバイトを対象として読み込むreads(InputStream)を追加
 *-->
 */
public final class StreamUtil {

	/** ストリーム行セパレータ **/
	public static final String DEFAULT_LINE_SEPARATOR = "\r\n";

	/** ストリームバッファサイズ */
	public static final int DEFAULT_STREAM_BUFFER_SIZE = 2048;

	/**
	 * コンストラクタ<br>
	 */
	private StreamUtil() {
		super();
	}

	/**
	 * 指定されたInputStreamオブジェクトから情報を文字列として取得します。<br>
	 * @param stream InputStreamオブジェクト
	 * @param charset キャラクタセット
	 * @return InputStreamオブジェクトから提供された文字列
	 * @throws IOException ストリーム操作時に入出力例外が発生した場合にスローされます
	 */
	public static String getStringValue(InputStream stream, String charset) throws IOException {
		BufferedReader reader = charset == null ? new BufferedReader(new InputStreamReader(stream)) : new BufferedReader(new InputStreamReader(stream, charset));
		//StringBuffer buffer = new StringBuffer();
		StringWriter sWriter = new StringWriter();
		BufferedWriter bWriter = new BufferedWriter(sWriter);
		while (reader.ready()) {
			String value = reader.readLine();
			//buffer.append(value);
			//buffer.append("\n");
			bWriter.write(value);
			bWriter.newLine();
			bWriter.flush();
		}
		//return buffer.toString();
		return sWriter.toString();
	}

	/**
	 * 指定されたInputStreamオブジェクトから情報を文字列として取得します。<br>
	 * @param stream InputStreamオブジェクト
	 * @return InputStreamオブジェクトから提供された文字列
	 * @throws IOException ストリーム操作時に入出力例外が発生した場合にスローされます
	 */
	public static String getStringValue(InputStream stream) throws IOException {
		return getStringValue(stream, null);
	}

	/**
	 * 入力ストリームから指定された長さのバイトデータを取得します。<br>
	 * 尚、指定されたサイズまでのデータが存在しなかった場合は、読み込めたデータ長のバイト配列が返却されます。<br>
	 * また、取得長に0以下の数値を指定した場合、全てのバイト長を対象として処理が行われます。<br>
	 * @param stream InputStreamオブジェクト
	 * @param length 取得するバイト長
	 * @return 取得されたバイトデータ
	 * @throws IOException ストリーム操作時に入出力例外が発生した場合にスローされます
	 */
	public static byte[] reads(InputStream stream, int length) throws IOException {
		//if (length <= 0) {
		//	return new byte[0];
		//}
		ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
		int total = 0;
		while (true) {
			int readsize = -1;
			if (length > 0) {
				readsize = length < total + DEFAULT_STREAM_BUFFER_SIZE ? length - total : DEFAULT_STREAM_BUFFER_SIZE;
			} else {
				readsize = DEFAULT_STREAM_BUFFER_SIZE;
			}
			byte[] data = new byte[readsize];
			int readedsize = stream.read(data);
			if (readedsize == -1) {
				break;
			}
			total += readedsize;
			byteOutputStream.write(data, 0, readedsize);
			if (length > 0) {
				if (total >= length) {
					break;
				}
			}
		}
		return byteOutputStream.toByteArray();
	}

	/**
	 * 入力ストリームから全てのバイトデータを取得します。<br>
	 * @param stream InputStreamオブジェクト
	 * @param length 取得するバイト長
	 * @return 取得されたバイトデータ
	 * @throws IOException ストリーム操作時に入出力例外が発生した場合にスローされます
	 */
	public static byte[] reads(InputStream stream) throws IOException {
		return reads(stream, -1);
	}

	/**
	 * 入力ストリームから単一行を読み込みます。<br>
	 * @param stream InputStreamオブジェクト
	 * @param separator 行デリミタ文字列(改行)
	 * @return 読み込まれた一行
	 * @throws IOException ストリーム操作時に入出力例外が発生した場合にスローされます
	 */
	public static String readLine(InputStream stream, String separator) throws IOException {
		ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
		ByteFIFOStack stack = new ByteFIFOStack(separator.length());
		int b = -1;
		while ((b = stream.read()) != -1) {
			byte[] data = stack.push(b);
			byteOutputStream.write(data);
			if (separator.equals(new String(stack.getBytes()))) {
				break;
			}
		}
		String value = new String(byteOutputStream.toByteArray());
		return value;
	}

	/**
	 * 入力ストリームから単一行を読み込みます。<br>
	 * @param stream InputStreamオブジェクト
	 * @return 読み込まれた一行
	 * @throws IOException ストリーム操作時に入出力例外が発生した場合にスローされます
	 */
	public static String readLine(InputStream stream) throws IOException {
		return readLine(stream, DEFAULT_LINE_SEPARATOR);
	}

	/**
	 * 出力ストリームに対して指定されたバイト配列を出力します。<br>
	 * 当メソッドは単純にOutputStreamのwriteメソッドの委譲メソッドで、readsメソッドとの
	 * 一貫性の為に設けられているメソッドです。<br>
	 * @param stream OutputStreamオブジェクト
	 * @param bytes 出力するバイト配列
	 * @throws IOException ストリーム操作時に入出力例外が発生した場合にスローされます
	 */
	public static void writes(OutputStream stream, byte[] bytes) throws IOException {
		stream.write(bytes);
	}

	/**
	 * 出力ストリームに対して単一行を出力します。<br>
	 * @param stream OutputStreamオブジェクト
	 * @param value 出力する単一行
	 * @param separator 行デリミタ文字列(改行)
	 * @throws IOException ストリーム操作時に入出力例外が発生した場合にスローされます
	 */
	public static void writeLine(OutputStream stream, String value, String separator) throws IOException {
		stream.write(value.getBytes());
		stream.write(separator.getBytes());
	}

	/**
	 * 出力ストリームに対して単一行を出力します。<br>
	 * @param stream OutputStreamオブジェクト
	 * @param value 出力する単一行
	 * @throws IOException ストリーム操作時に入出力例外が発生した場合にスローされます
	 */
	public static void writeLine(OutputStream stream, String value) throws IOException {
		writeLine(stream, value, DEFAULT_LINE_SEPARATOR);
	}

	/**
	 * 出力ストリームに対して空行(改行のみ)を出力します。<br>
	 * @param stream OutputStreamオブジェクト
	 * @throws IOException ストリーム操作時に入出力例外が発生した場合にスローされます
	 */
	public static void writeLine(OutputStream stream) throws IOException {
		writeLine(stream, "", DEFAULT_LINE_SEPARATOR);
	}
}
