package com.mockobjects.dynamic;

import java.util.ArrayList;
import java.util.Iterator;

import junit.framework.AssertionFailedError;

public class CallSequence extends CallCollection implements Callable, CallableAddable {
	
	private ArrayList expectedCalls = new ArrayList();
	private CallBag matchedCalls = new CallBag();
	int callIndex = 0;
	
    public void reset()
    {
        this.expectedCalls.clear();
        this.matchedCalls.reset();        
    }
    
	public Object call(Mock mock, String methodName, Object[] args) throws Throwable {
		if (expectedCalls.size() == 0) throw new AssertionFailedError("no methods defined on mock, received: " + DynamicUtil.methodToString(methodName, args));
		if (callIndex == expectedCalls.size()) throw new AssertionFailedError("mock called too many times, received: " + DynamicUtil.methodToString(methodName, args));
		
		Callable nextCall = (Callable)expectedCalls.get(callIndex++);
		if (nextCall.matches(methodName, args))
			return nextCall.call(mock, methodName, args);
			
		try {
			return matchedCalls.call(mock, methodName, args);
		} catch (AssertionFailedError ex) {
			throw createUnexpectedCallError(methodName, args);
		}
	}

    public String getDescription() {
        if (expectedCalls.isEmpty()) {
            return "no methods";
        } else {
            StringBuffer buf = new StringBuffer();
    
            buf.append("in sequence:\n");
    
            int j=0;
            for (Iterator i = expectedCalls.iterator(); i.hasNext();) {
                buf.append(((Callable)i.next()).getDescription());
                if (j++==(callIndex-1)) buf.append(" <<< Next Expected Call");
                buf.append("\n");
            }
    
            return buf.toString();
        }
    }
    
	public boolean matches(String methodName, Object[] args) {
		throw new AssertionFailedError("matches() operation not supported in CallSequence");
	}

	public void addExpect(Callable call) {
		expectedCalls.add(call);
	}
	
	public void addMatch(Callable call) {
		matchedCalls.addMatch(call);
	}

	public void verify() {
		for (Iterator iter = expectedCalls.iterator(); iter.hasNext();) {
			Callable callable = (Callable) iter.next();
			callable.verify();
		}
	}

}
