/*
 * 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.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.nio.charset.Charset;

import net.morilib.awk.nano.namespace.AwkNamespace;
import net.morilib.awk.nano.value.AwkValue;

/**
 * 1レコードを読み込むためのクラスです。
 *
 * @author MORIGUCHI, Yuichiro 2013/04/02
 */
public class RecordInputStream extends PushbackInputStream {

	private Charset encoding;
	private AwkNamespace ns;

	/**
	 * インスタンスを生成します。
	 * 
	 * @param in ラップされるReader
	 * @param ns 名前空間
	 */
	public RecordInputStream(InputStream in, AwkNamespace ns) {
		this(in, Charset.defaultCharset(), ns);
	}

	/**
	 * インスタンスを生成します。
	 * 
	 * @param in ラップされるReader
	 * @param encoding エンコード、nullのときはデフォルトのエンコード
	 * @param ns 名前空間
	 */
	public RecordInputStream(InputStream in, Charset encoding,
			AwkNamespace ns) {
		super(in);
		this.encoding = encoding != null ?
				encoding : Charset.defaultCharset();
		this.ns = ns;
	}

	private int rlength(String rs) {
		try {
			return Integer.parseInt(rs);
		} catch(NumberFormatException e) {
			return -1;
		}
	}

	/**
	 * 1行読み込みます。
	 * 
	 * @return 1行
	 * @throws IOException
	 */
	public String readLine() throws IOException {
		ByteArrayOutputStream b = new ByteArrayOutputStream();
		byte[] a;
		int x;

		while((x = read()) >= 0 && x != '\n' && x != '\r') {
			b.write(x);
		}
		if(x == '\r' && (x = read()) != '\n')  unread(x);
		a = b.toByteArray();
		return x < 0 && b.size() == 0 ? null : new String(a, encoding);
	}

	/**
	 * 1レコードを読み込みます。
	 * 
	 * @throws IOException
	 */
	public String readRecord() throws IOException {
		String rs = ns != null ? ns.getRS() : "\n", s, d = "", md;
		AwkValue pm = ns.getRoot().find("RMODE");
		StringBuffer b = null;
		int c, rn;
		byte[] ab;
		char[] ac;

		md = pm == null ? "" : pm.toString();
		if(md.equals("binary") && (rn = rlength(rs)) > 0) {
			ab = new byte[rn];
			c  = read(ab);
			return c < 0 ? null : new String(ab, 0, c, "ISO-8859-1");
		} else if(md.equals("fixed") && (rn = rlength(rs)) > 0) {
			ac = new char[rn];
			c  = EncoderDecoder.read(this, encoding, ac);
			return c < 0 ? null : new String(ac, 0, c);
		} else if(rs.equals("")) {
			while((s = readLine()) != null && s.equals(""));
			if(s == null)  return null;
			do {
				if(b == null)  b = new StringBuffer();
				b.append(d).append(s);
				d = "\n";
			} while((s = readLine()) != null && !s.equals(""));
			return b != null ? b.toString() : null;
		} else if(rs.equals("\n")) {
			return readLine();
		} else {
			while((c = EncoderDecoder.getchar(this, encoding)) >= 0) {
				if(c == rs.codePointAt(0)) {
					return b != null ? b.toString() : "";
				} else if(b == null) {
					b = new StringBuffer();
				}
				b.append((char)c);
			}
			return b != null ? b.toString() : null;
		}
	}

}
