/**
 * LOGICAL-PARADOX.ORG
 * Copyright (C)2004 satoshi akabane(akabane@logical-paradox.org)
 *
 */
package org.logical_paradox.common.util;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Iterator;

import org.logical_paradox.common.io.ByteStream;
import org.logical_paradox.common.io.ExpandableBuffer;

/**
 * ByteStreamの配列を格納するコレクション
 * このコレクション自体がByteStreamをimplementsしているため，バイト列に変換可能
 * 
 * @author satoshi akabane@logical-paradox.org
 * @version 1.0
 */
public abstract class ArrayListByteStream extends ArrayList implements ByteStream {
	/**
	 * デフォルトコンストラクタ
	 */
	public ArrayListByteStream() {
	}
	/**
	 * コンストラクタ
	 * 
	 * @param bs 生成時に追加するオブジェクトの配列
	 */
	public ArrayListByteStream(ByteStream[] bs) {
		add(bs);
	}
	/**
	 * コンストラクタ
	 * @param bs 生成時に復元するオブジェクトのバイト列
	 */
	public ArrayListByteStream(byte[] bs) {
		byteStreamToObject(bs);
	}
	/**
	 * コレクションの内容を全て一括してバイト列に変換する
	 * バイト変換処理は，格納されているByteStreamのbyteStream()メソッドで実行される．<br>
	 * また，変換されたバイト列の先頭に，要素数が格納される(4バイト)
	 * 
	 * @return バイト列
	 */
	public byte[] byteStream() {
		// 拡張可能バッファを生成する
		ExpandableBuffer buffer = new ExpandableBuffer();
		try {
			// 要素の出力
			synchronized(this) {
				byte[] element = null;
				for(Iterator it = iterator(); it.hasNext();) {
					ByteStream bs = (ByteStream)it.next();
					element = bs.byteStream();
					// 要素のバイトサイズをintで(4バイト) BIG endianで格納する
					buffer.write(element.length & 0xff000000);
					buffer.write(element.length & 0x00ff0000);
					buffer.write(element.length & 0x0000ff00);
					buffer.write(element.length & 0x000000ff);
					buffer.write(element);					// 要素のバイト列
				}
			}
		} catch(IOException ioe) {
			ioe.printStackTrace();
		}

		return buffer.byteStream();
	}
	/**
	 * バイト列からオブジェクトの配列を復元する
	 * このクラスのbyteStream()によって生成されたバイト列以外は復元できない
	 * @param bstream バイト列
	 */
	protected void byteStreamToObject(byte[] bstream) {
		ByteBuffer buffer = ByteBuffer.allocate(bstream.length).order(ByteOrder.BIG_ENDIAN);
		buffer.put(bstream);
		buffer.rewind();

		// オブジェクトの復元
		while(buffer.hasRemaining()) {
			int objectByteSize = buffer.getInt();
			byte[] objectBody = new byte[objectByteSize];
			buffer.get(objectBody);
			
			add(buildByteStreamObject(objectBody));
		}
	}
	/**
	 * バイト列からオブジェクトを復元する
	 * 何が具体的に復元されるかは，サブクラスで実装される
	 * @param bstream バイト列
	 * @return 復元されたオブジェクト
	 */
	protected abstract ByteStream buildByteStreamObject(byte[] bstream);
}
