/*
 * Copyright 2013 Yuichiro Moriguchi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.morilib.awk.nano.misc;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;

/**
 * バイナリストリームをエンコード・デコードする
 * ユーティリティクラスです。
 *
 * @author MORIGUCHI, Yuichiro 2013/04/27
 */
public final class EncoderDecoder {

	private EncoderDecoder() {}

	//
	static int _getchar(InputStream ins,
			Charset encoding) throws IOException {
		CharsetDecoder cd = encoding.newDecoder();
		ByteBuffer bb = ByteBuffer.allocate(8);
		CharBuffer cb = CharBuffer.allocate(8);
		byte[] a = new byte[8];
		int b;

		for(int i = 0; i < a.length; i++) {
			if((b = ins.read()) < 0)  return -1;
			a[i] = (byte)b;
			bb.put(a, 0, i + 1).flip();
			if(cd.decode(bb, cb, true).isUnderflow()) {
				cd.flush(cb);
				if(cb.flip().hasRemaining())  return cb.get();
			} else {
				bb.clear();
			}
		}
		return -1;
	}

	/**
	 * InputStreamから読み込み、1文字デコードして返します。
	 * 
	 * @param ins InputStream
	 * @param encoding エンコード、nullのときはデフォルトのエンコード
	 * @return デコードされた文字、ないときは-1
	 * @throws IOException
	 */
	public static int getchar(InputStream ins,
			Charset encoding) throws IOException {
		return _getchar(ins,
				encoding != null ?
						encoding : Charset.defaultCharset());
	}

	/**
	 * InputStreamから読み込み、デフォルトのデコーダで
	 * 1文字デコードして返します。
	 * 
	 * @param ins InputStream
	 * @return デコードされた文字、ないときは-1
	 * @throws IOException
	 */
	public static int getchar(InputStream ins) throws IOException {
		return getchar(ins, Charset.defaultCharset());
	}

	/**
	 * InputStreamから読み込み、デコードして配列に格納します。
	 * 
	 * @param ins InputStream
	 * @param encoding エンコード、nullのときはデフォルトのエンコード
	 * @param a 結果を格納する配列
	 * @param begin  配列の開始オフセット
	 * @param length 格納する文字数
	 * @return 読み込まれた文字数、読み込まれなかったときは-1
	 * @throws IOException
	 */
	public static int read(InputStream ins, Charset encoding, char[] a,
			int begin, int length) throws IOException {
		int c;

		for(int i = begin; i < begin + length; i++) {
			if((c = getchar(ins, encoding)) < 0) {
				return i > begin ? i - begin : -1;
			} else {
				a[i] = (char)c;
			}
		}
		return length;
	}

	/**
	 * InputStreamから読み込み、デコードして配列に格納します。
	 * 
	 * @param ins InputStream
	 * @param encoding エンコード、nullのときはデフォルトのエンコード
	 * @param a 結果を格納する配列
	 * @return 読み込まれた文字数、読み込まれなかったときは-1
	 * @throws IOException
	 */
	public static int read(InputStream ins, Charset encoding,
			char[] a) throws IOException {
		return read(ins, encoding, a, 0, a.length);
	}

	/**
	 * InputStreamから読み込み、デフォルトのデコーダで
	 * デコードして配列に格納します。
	 * 
	 * @param ins InputStream
	 * @param a 結果を格納する配列
	 * @param begin  配列の開始オフセット
	 * @param length 格納する文字数
	 * @return 読み込まれた文字数、読み込まれなかったときは-1
	 * @throws IOException
	 */
	public static int read(InputStream ins, char[] a,
			int begin, int length) throws IOException {
		return read(ins, null, a, begin, length);
	}

	/**
	 * InputStreamから読み込み、デフォルトのデコーダで
	 * デコードして配列に格納します。
	 * 
	 * @param ins InputStream
	 * @param a 結果を格納する配列
	 * @return 読み込まれた文字数、読み込まれなかったときは-1
	 * @throws IOException
	 */
	public static int read(InputStream ins,
			char[] a) throws IOException {
		return read(ins, null, a, 0, a.length);
	}

}
