package jp.cssj.cti.helpers;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 各断片の位置情報を得るためのContentBuilderのラッパーです。
 * 
 * @author <a href="mailto:miyabe at gnn.co.jp">MIYABE Tatsuhiko </a>
 * @version $Id: PositionalContentBuilderWrapper.java,v 1.1 2005/03/26 10:22:30
 *          harumanx Exp $
 */
public class PositionalContentBuilderWrapper implements
		PositionalContentBuilder {
	private static final Logger LOGGER = Logger
			.getLogger(PositionalContentBuilderWrapper.class.getName());

	protected final ContentBuilder builder;

	protected List frgs = new ArrayList();

	protected Fragment first = null, last = null;

	protected class Fragment {
		public final int id;

		public Fragment prev = null, next = null;

		public long length = 0;

		public Fragment(int id) {
			this.id = id;
		}
	}

	public PositionalContentBuilderWrapper(ContentBuilder builder) {
		this.builder = builder;
	}

	protected int nextId() {
		return this.frgs.size();
	}

	protected Fragment getFragment(int id) {
		if (id < 0 || id >= this.frgs.size()) {
			return null;
		}
		return (Fragment) this.frgs.get(id);
	}

	protected void putFragment(int id, Fragment frg) {
		assert (id == this.frgs.size());
		this.frgs.add(frg);
	}

	public Info getPositionalInfo() {
		final long[] idToPosition = new long[this.frgs.size()];
		long position = 0;
		Fragment frg = this.first;
		while (frg != null) {
			idToPosition[frg.id] = position;
			position += frg.length;
			frg = frg.next;
		}
		return new Info() {
			public long getPosition(int id) {
				return idToPosition[id];
			}
		};
	}

	public void addFragment() throws IOException {
		int id = this.nextId();
		Fragment frg = new Fragment(id);
		if (this.first == null) {
			this.first = frg;
		} else {
			this.last.next = frg;
			frg.prev = this.last;
		}
		this.putFragment(id, frg);
		this.last = frg;
		this.builder.addFragment();
	}

	public void insertFragmentBefore(int anchorId) throws IOException {
		int id = this.nextId();
		Fragment anchor = this.getFragment(anchorId);
		if (anchor == null) {
			String message = "アンカー番号" + anchorId + "は存在しません。";
			throw new IOException(message);
		}
		Fragment frg = new Fragment(id);
		this.putFragment(id, frg);
		frg.prev = anchor.prev;
		frg.next = anchor;
		anchor.prev.next = frg;
		anchor.prev = frg;
		if (this.first == anchor) {
			this.first = frg;
		}
		this.builder.insertFragmentBefore(anchorId);
	}

	public void write(int id, byte[] b, int off, int len) throws IOException {
		Fragment frg = this.getFragment(id);
		if (frg == null) {
			String message = "断片番号" + id + "は存在しません。";
			throw new IOException(message);
		}
		frg.length += len;
		this.builder.write(id, b, off, len);
	}

	public void close(int id) throws IOException {
		this.builder.close(id);
	}

	public void finish() throws IOException {
		if (LOGGER.isLoggable(Level.FINE)) {
			int total = this.frgs.size();
			LOGGER.fine(total + "個のフラグメントが生成されました。");
		}
		this.first = null;
		this.last = null;
		this.frgs = null;
		this.builder.finish();
	}
}