package com.ibm.able.examples.conversation;

import java.rmi.RemoteException;
import java.util.Iterator;
import java.util.Vector;

import javax.agent.AgentName;
import javax.agent.Locator;
import javax.agent.service.directory.AgentDescription;

import com.ibm.able.Able;
import com.ibm.able.AbleException;
import com.ibm.able.conversation.AbleConversationManager;
import com.ibm.able.conversation.AblePlatformConversationAgent;
import com.ibm.able.platform.AblePlatform;

/**
 * The ChatterboxAgent2 class is an second example of an AblePlatformConversationAgent.
 * It is capable of executing the "Chat3" conversation, like its close relative the
 * ChatterboxAgent. Its only difference from that agent is that it installs a different
 * decision logic module for that conversation.
 */
public class ChatterboxAgent2 extends AblePlatformConversationAgent {

	// ===========================================
	// Class variables
	// ===========================================

	public static final String ABLE_AGENT_TYPE = "Chatterbox";

	// ===========================================
	// Instance variables
	// ===========================================

	int totalConversations = 1;
	int conversationCount = 0;
	boolean testMessageEnabled = false;
	boolean conversationEnabled = true;
	AbleChat3GuiLogic chat3DecisionLogic = null;
	
	// ===========================================
	// Constructors
	// ===========================================
	/**
	 * @throws RemoteException
	 * @throws AbleException
	 */
	public ChatterboxAgent2(String instanceName)
		throws RemoteException, AbleException {
		super(instanceName);
		setAgentType(ABLE_AGENT_TYPE);
		
		chat3DecisionLogic = new AbleChat3GuiLogic(this);
	}

	// ===========================================
	// Properties
	// ===========================================

	/**
	 * Returns the threshold on the number of conversation this agent
	 * may participate in.
	 * 
	 * @return The total number of conversations this agent may have.
	 */
	public int getTotalConversations() {
		return totalConversations;
	}

	/**
	 * @param i
	 */
	public void setTotalConversations(int i) {
		totalConversations = i;
	}

	/**
	 * Indicates whether this agent may initiate conversations.
	 * 
	 * @return true if this agent may initiate converstions, false if not.
	 */
	public boolean isConversationEnabled() {
		return conversationEnabled;
	}

	/**
	 * Indicates whether this agent may send a test message.
	 * 
	 * @return true if this agent may send a test message, false if not.
	 */
	public boolean isTestMessageEnabled() {
		return testMessageEnabled;
	}

	/**
	 * @param b
	 */
	public void setConversationEnabled(boolean b) {
		conversationEnabled = b;
	}

	/**
	 * @param b
	 */
	public void setTestMessageEnabled(boolean b) {
		testMessageEnabled = b;
	}

	// ===========================================
	// Operation
	// ===========================================

	/* (non-Javadoc)
	 * @see com.ibm.able.AbleBean#init()
	 */
	public void init() throws AbleException {
//		this.getLogger().text(Able.MSG_INFO, this, "init", "start");

		super.init();
		this.getLogger().text(
			Able.MSG_INFO,
			this,
			"init",
			"[" + this.getName() + "] CPs loaded: " + listKnownCpNames());
			
		super.installTargetForCp("Chat3", "Role1", chat3DecisionLogic);
		super.installTargetForCp("Chat3", "Role2", chat3DecisionLogic);
			
//		this.getLogger().text(Able.MSG_INFO, this, "init", "done");
	}

	/* (non-Javadoc)
	 * @see com.ibm.able.AbleBean#process()
	 */
	public void process() throws AbleException {
		super.process();

		// this.getLogger().text(Able.MSG_INFO, this, "process", "start");

		boolean sendTest = decideToSendTestMessage();
		boolean startConv =
			(sendTest == true) ? false : decideToStartConversation();

		if (startConv == true) {
			logMessage(Able.MSG_INFO,
			"process", "decided to start conversation");
		}
		else if (sendTest == true) {
			logMessage(
				Able.MSG_INFO,
			"process",
				"decided to send test message");
		}
		else {
//			logMessage(Able.MSG_INFO,
//				"process",
//				"decided not to send any messages");
		}

		AgentDescription newPartner = null;
		if (sendTest == true || startConv == true) {
			newPartner = selectConversationPartner();
			if (newPartner != null) {
//				logMessage(Able.MSG_INFO,
//				"process",
//					"found partner");
			}
			else {
				logMessage(
					Able.MSG_INFO,
				"process",
					"no partner found");
			}
		}

		if (sendTest == true && newPartner != null) {
			logMessage(
				Able.MSG_INFO,
			"process",
				"sending test message");
			sendTestMessage(newPartner);
		}
		else if (startConv == true && newPartner != null) {
			logMessage(
				Able.MSG_INFO,
				"process",
				"starting conversation");
			conversationCount++;
			startConversation(newPartner, "Chat3", "Role1");
		}

//		this.getLogger().text(Able.MSG_INFO, this, "process", "done");
	}

	protected boolean decideToSendTestMessage() {
		return testMessageEnabled;
	}

	protected boolean decideToStartConversation() {
		if (conversationCount < totalConversations) {
			return conversationEnabled;
		}
		else {
			return false;
		}
	}

	protected AgentDescription selectConversationPartner() {

		// Use the Agent Directory Service to look up agents with the 
		// desired attributes.   
		String lookupAttr = AblePlatform.AbleAgentType;
		String lookupValue = ChatterboxAgent2.ABLE_AGENT_TYPE;

		AgentDescription[] lookupResults = null;
		try {
			lookupResults = lookUpAgent(lookupAttr, lookupValue);
//			logMessage(
//				Able.MSG_INFO,
//				"selectConversationPartner",
//				"Found " + lookupResults.length + " agents to talk to");
		}
		catch (Exception ex) {
			logException("selectConversationPartner", ex);
		}

		// Select a partner from the list.
		AgentDescription selection = null;
		if (lookupResults != null && lookupResults.length > 0) {
			// Presumably, one of the elems in lookupResults is this agent's own description.
			// Create a new list that does NOT contain it.
			Vector possibilities = new Vector();
			for (int i=0; i<lookupResults.length; i++) {
				if (myJasAgentName.equals(lookupResults[i].getAgentName()) == false)
					possibilities.add(lookupResults[i]);
			}

			// Choose one of the possibilites at random.
			if (possibilities.size() > 0) {
				int selectionIndex = (int)(Math.random() * possibilities.size());
				selection = (AgentDescription)possibilities.elementAt(selectionIndex);
			}
		}
			
//		this.getLogger().text(
//			Able.MSG_INFO,
//			this,
//			this.getName() + ":selectConversationPartner",
//			"Selected " + selection);
		return selection;
	}

	protected void sendTestMessage(AgentDescription partnerDesc) {
		AgentName partnerAgentName = partnerDesc.getAgentName();

		// Get the RMI Locator for the agent.
		Locator rmiLocator = getLocatorForType(partnerDesc, "rmi");

		// Send the message.
		if (rmiLocator != null) {
			try {
				sendTransportMessage(
					rmiLocator,
					partnerAgentName,
					"Hello, partner!");
			}
			catch (Exception ex) {
				logException(
					"sendTestMessage",
					ex);
			}
		}

	}

	// ===========================================
	// Debugging
	// ===========================================

	public String listKnownCpNames() {
		AbleConversationManager convMgr = super.conversationManager;
		Iterator iter =
			convMgr
				.getConversationPolicyHandlerFactory()
				.getPolicyNames()
				.iterator();
		StringBuffer sbuf = new StringBuffer();
		String delimiter = " ";
		while (iter.hasNext()) {
			sbuf.append((String)iter.next());
			if (iter.hasNext())
				sbuf.append(delimiter);
		}
		return sbuf.toString();
	}

}
