package jp.cssj.cti.ctip.driver;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;

import jp.cssj.cti.ctip.CTIPUtils;
import jp.cssj.cti.ctip.ContentProducer;
import jp.cssj.cti.helpers.RequestConsumer;

/**
 * @author <a href="mailto:miyabe at gnn.co.jp">MIYABE Tatsuhiko </a>
 * @version $Id: ContentProducerImpl.java,v 1.2 2005/03/26 13:21:15 harumanx Exp $
 */
public class ContentProducerImpl implements ContentProducer {
	protected final String encoding;

	protected final String host;

	protected final int port;

	protected SocketChannel channel;

	public ContentProducerImpl(String host, int port, String encoding)
			throws IOException {
		this.encoding = encoding;
		this.host = host;
		this.port = port;
	}

	public RequestConsumer connect() throws IOException {
		this.channel = SelectorProvider.provider().openSocketChannel();
		this.channel.connect(new InetSocketAddress(this.host, this.port));
		this.channel.configureBlocking(true);

		byte[] header = ("CTIP/1.0 " + this.encoding + "\n")
				.getBytes("ISO-8859-1");
		CTIPUtils.writeAll(this.channel, ByteBuffer.wrap(header));
		return new CTIPRequestConsumer(this.channel, this.encoding);
	}

	private byte type;

	private int id, anchorId;

	private int progress;

	private byte level;

	private String error;

	private ByteBuffer data;

	private ByteBuffer destInt = ByteBuffer.allocate(4);

	private ByteBuffer destShort = ByteBuffer.allocate(2);

	private ByteBuffer destByte = ByteBuffer.allocate(1);

	protected void close() throws IOException {
		if (this.channel != null) {
			this.channel.close();
			this.channel = null;
		}
	}

	public boolean next() throws IOException {
		int payload = CTIPUtils.readInt(this.channel, this.destInt);
		if (payload == 0) {
			this.close();
			return false;
		}

		this.type = CTIPUtils.readByte(this.channel, this.destByte);
		switch (this.type) {
		case ContentProducer.ADD:
			break;

		case ContentProducer.INSERT:
			this.anchorId = CTIPUtils.readInt(this.channel, this.destInt);
			break;

		case ContentProducer.DATA:
			this.id = CTIPUtils.readInt(this.channel, this.destInt);
			this.progress = CTIPUtils.readInt(this.channel, this.destInt);
			payload -= 9;
			this.data = ByteBuffer.allocate(payload);
			CTIPUtils.readAll(this.channel, this.data);
			this.data.position(0);
			break;

		case ContentProducer.ERROR:
			this.level = CTIPUtils.readByte(this.channel, this.destByte);
			this.error = CTIPUtils.readString(this.channel, this.destShort,
					this.encoding);
			break;

		default:
			throw new IOException("Bad response: type " + this.type);
		}

		return true;
	}

	public int getId() throws IOException {
		return this.id;
	}

	public int getAnchorId() throws IOException {
		return this.anchorId;
	}

	public byte getType() throws IOException {
		return this.type;
	}

	public long getProgress() throws IOException {
		return this.progress;
	}

	public String getMessage() throws IOException {
		return this.error;
	}

	public byte getLevel() throws IOException {
		return this.level;
	}

	public int read(byte[] b, int off, int len) throws IOException {
		if (this.data.remaining() <= 0) {
			return -1;
		}
		len = Math.min(len, this.data.remaining());
		this.data.get(b, off, len);
		return len;
	}
}