package jp.cssj.cti.ctip;

import java.io.IOException;
import java.io.OutputStream;

import jp.cssj.cti.CTISession;
import jp.cssj.cti.helpers.ContentBuilder;
import jp.cssj.cti.helpers.ErrorHandler;
import jp.cssj.cti.helpers.FileContentBuilder;
import jp.cssj.cti.helpers.ProgressListener;
import jp.cssj.cti.helpers.RequestConsumer;
import jp.cssj.cti.helpers.RequestConsumerOutputStream;

/**
 * @author <a href="mailto:miyabe at gnn.co.jp">MIYABE Tatsuhiko </a>
 * @version $Id: AbstractCTIPSession.java,v 1.2 2005/08/18 04:51:26 harumanx Exp $
 */
public abstract class AbstractCTIPSession implements CTISession {
	private final byte[] buff = new byte[1024];

	protected final ContentProducer producer;

	protected final RequestConsumer request;

	protected OutputStream out = System.out;

	protected ErrorHandler errorHandler = null;

	protected ProgressListener progressListener = null;

	protected int state = 1;

	public AbstractCTIPSession(ContentProducer producer, RequestConsumer request) {
		this.producer = producer;
		this.request = request;
	}

	public void setOutput(OutputStream out, String mimeType) throws IOException {
		if (this.state >= 2) {
			throw new IllegalStateException("既に本体が変換されています。");
		}
		this.setProperty("output.type", mimeType);
		this.out = out;
	}

	public void setErrorHandler(ErrorHandler eh) {
		if (this.state >= 2) {
			throw new IllegalStateException("既に本体が変換されています。");
		}
		this.errorHandler = eh;
	}

	public void setProgressListener(ProgressListener l) {
		if (this.state >= 2) {
			throw new IllegalStateException("既に本体が変換されています。");
		}
		this.progressListener = l;
	}

	public void setProperty(String key, String value) throws IOException {
		if (this.state >= 2) {
			throw new IllegalStateException("既に本体が変換されています。");
		}
		this.request.property(key, value);
	}

	public void includeResource(String uriPattern) throws IOException {
		this.setProperty("ctip.include", uriPattern);
	}

	public void excludeResource(String uriPattern) throws IOException {
		this.setProperty("ctip.exclude", uriPattern);
	}

	public void formatMain(String uri) throws IOException {
		this.setProperty("ctip.main", uri);
		this.state = 2;
		this.request.end();
		FileContentBuilder builder = new FileContentBuilder(this.out);
		try {
			while (this.buildNext(builder)) {
				// do nothing
			}
		} finally {
			builder.finish();
		}
		this.state = 3;
	}

	public OutputStream sendResource(String uri, String mimeType,
			String encoding) throws IOException {
		if (this.state >= 2) {
			throw new IllegalStateException("既に本体が変換されています。");
		}
		this.request.resource(uri, mimeType, encoding);
		return new RequestConsumerOutputStream(this.request);
	}

	public boolean buildNext(ContentBuilder builder) throws IOException {
		if (this.producer.next()) {
			switch (this.producer.getType()) {
			case ContentProducer.ADD: {
				builder.addFragment();
			}
				break;

			case ContentProducer.INSERT: {
				int anchorId = this.producer.getAnchorId();
				builder.insertFragmentBefore(anchorId);
			}
				break;

			case ContentProducer.DATA: {
				int id = this.producer.getId();
				if (this.progressListener != null) {
					this.progressListener.progress(this.producer.getProgress());
				}
				for (int len = this.producer.read(this.buff, 0,
						this.buff.length); len != -1; len = this.producer.read(
						this.buff, 0, this.buff.length)) {
					builder.write(id, this.buff, 0, len);
				}
			}
				break;

			case ContentProducer.ERROR: {
				if (this.errorHandler != null) {
					byte level = this.producer.getLevel();
					String message = this.producer.getMessage();
					this.errorHandler.message(level, message);
				}
			}
				break;
			}
			return true;
		}
		return false;
	}
}