/*
 * Galatea Dialog Manager:
 * (c)2003 Takuya NISHIMOTO (nishi@hil.t.u-tokyo.ac.jp)
 * Based on Phoenix By Takuya NISHIMOTO and Mitsuhiro KIZU
 *
 * $Id: OutItemQueueManager.java,v 1.5 2008/02/14 02:00:07 nishi Exp $
 */
package galatea.io;

import galatea.logger.Logger;
import galatea.outitem.OutItem;

import java.util.ArrayList;

public class OutItemQueueManager            
{
	private Logger dbg = new Logger("OQM", 0);
	private DeviceManager deviceManager_;
	private int outputState_ = 0;
	private int outputCount_ = 0;
	private int prevOutputCount_ = 0;
	private OutItem currOutItem_ = null;
	private ArrayList<OutItem> queue_; 
	private boolean currOutItemIsValid_ = false;
	private boolean currOutQueueIsValid_ = false;
	
	private final int STATE_NOT_STARTED = 0;
	private final int STATE_WAIT_FOR_OUTPUT_BUSY = 1;
	private final int STATE_WAIT_FOR_OUTPUT_READY = 2;
	private final int STATE_WAIT_FOR_OUTPUT_STOPPED = 3;
	
	public OutItemQueueManager(DeviceManager d) {
		deviceManager_ = d;
		queue_ = new ArrayList<OutItem>();
	}
	
	public synchronized void setCurrOutQueueIsValid(boolean b) {
		currOutQueueIsValid_ = b;
		dbg.print("currOutQueueIsValid: "+b);
	}
	
	public synchronized boolean getCurrOutQueueIsValid() {
		return currOutQueueIsValid_;
	}
	
//	private boolean doOutput(OutItem item) {
//		return deviceManager.doOutput(item);
//	}
	
	
//	private void doStop(OutItem item) {
//		deviceManager.doStop(item);
//	}
	
	
	public synchronized void setCurrOutItemIsValid(boolean b) {
		currOutItemIsValid_ = b;
		//dbg.print("IC: currOutItemIsValid: "+b);
	}
	
//	private void resetOutputCount()
//	{
//		outputCount = 0;
//	}
	
	public synchronized void processDeviceEvent(DeviceEvent evt) {
		dbg.print("processDeviceEvent " + evt.toString());
		if (evt.isOutputBusyEvent()) {
			incrementOutputCount();	    
		} else if (evt.isOutputReadyEvent()) {
			decrementOutputCount();	    
		}
	}

	private void incrementOutputCount() {
		outputCount_ ++;
		dbg.print("OQM: (increment) outputCount=" + outputCount_);
	}
	
	private void decrementOutputCount() {
		outputCount_ --;
		dbg.print("OQM: (decrement) outputCount=" + outputCount_);
	}
	
	public synchronized void discard() {
		dbg.print("OQM: queue discard");
		queue_.clear();
	}
	
	public synchronized void enqueue(OutItem o) {
		queue_.add(o);
		dbg.print("OQM: queue added (" + o.getClass().getName() 
				+ ") total:" + queue_.size() );
	}
	
	private OutItem dequeue() {
		OutItem ret;
		if (queue_.isEmpty()) {
			return null;
		} else {
			ret = (OutItem)queue_.remove(0);
			dbg.print("OQM: dequeue() removed (" + ret.getClass().getName() 
					+ ") total:" + queue_.size() );
			return ret;
		}
	}
	
	
	public synchronized void removeDelayedOutputItems() {
		ArrayList<OutItem> newvec = new ArrayList<OutItem>();
		for (int i = 0; i < queue_.size(); i++ ) {
			OutItem o = queue_.get(i);
			if (o.isInstant() || o.isImportant() ) {
				newvec.add(o);
			}
		}
		queue_ = newvec;
		dbg.print("OQM: removeDelayedOutputItems() total:" + queue_.size() );
	}
	

	// returns false if dequeue item is null
	public synchronized boolean doOutputIteration() {
		OutItem item;
		switch (getOutputState()) {
		case STATE_NOT_STARTED:
			// dbg.print("OQM: [0] starting output");
			if (queue_.isEmpty()) {
				return false;
			}
			item = dequeue();
			if ( item == null ) {
				dbg.print("OQM: doOutputIteration() dequeue item is null");
				return false;
			}
			if ( item.isInstant() ) {
				dbg.print("OQM: output instant: " + item.toString());
				boolean success = deviceManager_.doOutput(item);
				if (!success) {
					return false;
				}
			} else {
				dbg.print("OQM: output non-instant: " + item.toString());
				boolean success = deviceManager_.doOutput(item);
				if (!success) {
					return false;
				}
				setCurrOutItemIsValid(true);
				setOutputState(STATE_WAIT_FOR_OUTPUT_BUSY);
				setCurrOutItem(item);
				setPrevOutputCount(outputCount_);
			}
			break;

		case STATE_WAIT_FOR_OUTPUT_BUSY:
			// dbg.print("OQM: [1] waiting for output busy");
			if ( ! currOutItemIsValid_ ) {
				deviceManager_.doStop(getCurrOutItem());
				setOutputState(STATE_WAIT_FOR_OUTPUT_STOPPED);
				break;
			}
			if (outputCount_ == getPrevOutputCount() + 1) {
				setOutputState(STATE_WAIT_FOR_OUTPUT_READY);
				break;
			}
			break;
		
		case STATE_WAIT_FOR_OUTPUT_READY:
			// dbg.print("OQM: [2] waiting for output ready");
			if ( ! currOutItemIsValid_ ) {
				deviceManager_.doStop(getCurrOutItem());
				setOutputState(STATE_WAIT_FOR_OUTPUT_STOPPED);
				break;
			}
			if (outputCount_ == getPrevOutputCount()) {
				setOutputState(STATE_NOT_STARTED);
				break;
			}
			break;
		
		case STATE_WAIT_FOR_OUTPUT_STOPPED:
			// dbg.print("OQM: [3] waiting for output stop done");
			if (outputCount_ == getPrevOutputCount()) {
				setOutputState(STATE_NOT_STARTED);
				break;
			}
			break;
		
		default:
			break;
		}
		
		return true;
	}
	
	private void setOutputState(int outputState) {
		this.outputState_ = outputState;
		switch (this.outputState_) {
		case STATE_NOT_STARTED:
			dbg.print("OQM: [outputState 0] starting output");
			break;
		case STATE_WAIT_FOR_OUTPUT_BUSY:
			dbg.print("OQM: [outputState 1] waiting for output busy");
			break;
		case STATE_WAIT_FOR_OUTPUT_READY:
			dbg.print("OQM: [outputState 2] waiting for output ready");
			break;
		case STATE_WAIT_FOR_OUTPUT_STOPPED:
			dbg.print("OQM: [outputState 3] waiting for output stop done");
			break;
		default:
			break;
		}
	}
	
	public synchronized OutItem getCurrOutItem() {
		return currOutItem_;
	}

	private void setCurrOutItem(OutItem currOutItem) {
		this.currOutItem_ = currOutItem;
	}
	
	private synchronized int getOutputState() {
		return outputState_;
	}
	
	private int getPrevOutputCount() {
		return prevOutputCount_;
	}
	
	private void setPrevOutputCount(int prevOutputCount) {
		this.prevOutputCount_ = prevOutputCount;
	}
	
	public synchronized void stopCurrentOutput() {
		if (getCurrOutItem() != null) {
			deviceManager_.doStop(getCurrOutItem());
			setOutputState(STATE_WAIT_FOR_OUTPUT_STOPPED);
		}
	}
	
//	public void stopOutput() {
//		setCurrOutItemIsValid(false);
//		setCurrOutQueueIsValid(false);
//	}
	
	public synchronized String toString() {
		String result = "";
		result += "count: " + queue_.size() + "\n";
		for (int i=0; i<queue_.size(); i++) {
			result += "[item " + i + "] ";
			result += queue_.get(i).toString() + "\n";
		}
		return result;
	}

}
