/* Author: Jeff Dalton <J.Dalton@ed.ac.uk>
 * Updated: Tue Dec  4 02:25:35 2001 by Jeff Dalton
 * Copyright: (c) 2001, AIAI, University of Edinburgh
 */

package ix.ideel;

import javax.swing.*;

import java.util.*;

import javax.swing.JPanel;

import ix.iface.domain.DomainParser;
import ix.iface.util.LogoPanel;

import ix.icore.domain.*;
import ix.icore.domain.event.*;
import ix.icore.process.StatusValues;
import ix.icore.*;

import ix.ichat.ChatMessage;

import ix.util.*;
import ix.util.lisp.*;


/** 
 * The generic I-DEEL class and application main program 
 */

public class BasicIdeel extends IXAgent implements StatusValues {

    protected Domain domain = new Domain();

    protected IdeelFrame frame;

    protected IdeelController controller = new IdeelController(this);

    protected IssueViewingTable issueViewer;

    public Object getAgentIPCName() { return ipcName; }
    public Object getAgentSymbolName() { return symbolName; }

    // Some customizable fields
    protected String displayName = "I-DEEL";
    protected String symbolName = "I-DEEL";
    protected String logoLine1 = "I-X Dynamic Event and Execution List";
    protected String logoLine2 = "Based on I-X Technology";
    protected String logoImage = "images/ip2-logo.gif";

    protected String ipcName = "IDEEL";
    protected String domainName = null;

    public BasicIdeel() {
	super();
    }

    /**
     * Main program.
     */
    public static void main(String[] argv) {

	Util.printGreeting("Basic I-DEEL");

	new BasicIdeel().mainStartup(argv);

    }

    /**
     * Command-line argument processing for arguments used by all
     * versions of I-DEEL.
     */
    protected void processCommandLineArguments() {
	if (Parameters.haveParameter("ipc-name")) {
	    ipcName = Parameters.getParameter("ipc-name");
	    Debug.noteln("Using IPC name", ipcName);
	}

	// N.B. need to do this after setting ipcName, because
	// super method sets up IPC.
	super.processCommandLineArguments();

	displayName = Parameters.getParameter("display-name", displayName);
	symbolName  = Parameters.getParameter("symbol-name", symbolName);
	logoLine1   = Parameters.getParameter("logo-line-1", logoLine1);
	logoLine2   = Parameters.getParameter("logo-line-2", logoLine2);
	logoImage   = Parameters.getParameter("logo-image", logoImage);

	if (Parameters.haveParameter("domain"))
	    domainName = Parameters.getParameter("domain");

    }

    /**
     * Completes basic I-DEEL setup and initialization.
     */
    public void startup() {
	frame = new IdeelFrame(this);
	frame.setUp();
	issueViewer = frame.issueViewer;
	issueViewer.setIssueManager(controller);
	controller.setDomain(domain);
	controller.addControllerListener(issueViewer);
	readDomain(domain);
	addIdeelIssueHandlers();
	frame.becomeVisible();
    }

    /**
     * Read in any default domain description.
     */
    protected void readDomain(Domain domain) {
	if (domainName != null) {
	    try {
		DomainParser.makeParser(domainName).readDomain(domain);
	    }
	    catch (Exception e) {
		Debug.noteException(e);
	    }
	}
    }

    /**
     * Makes the LogoPanel for the application's main frame.
     * This method is in this class to make it easier to define
     * versions that have a different logo panel.
     */
    protected JPanel makeLogoPanel() {
	return new LogoPanel(symbolName, logoLine1, logoLine2, logoImage);
    }

    /**
     * Called when the main frame is set up to add items to the frame
     * menu bar's "Test" menu.  This method is in this class, rather than
     * in the frame class, to make it easier to define versions that have
     * different test items.
     */
    protected void addTestMenuItems() {
    }


    /**
     * Called to restore the initial state.
     */
    public void reset() {
	// /\/: Things should register for reset instead of being known here.
	controller.reset();	// should contoller tell issueViewer? /\/
	issueViewer.reset();
	frame.validate();
    }

    /**
     * Handles chat messages in addition to the types of input handled
     * by the super method.
     */
    public void handleInput(IPC.InputMessage message) {
	Object contents = message.getContents();
        if (contents instanceof ChatMessage) {
	    handleNewChatMessage((ChatMessage)contents);
        }
	else {
	    super.handleInput(message);
        }
    }

    /**
     * Handles new issues from external sources.
     */
    public void handleNewIssue(Issue issue) {
        controller.addIssue(issue);
    }

    /**
     * Handles new reports from external  sources.
     */
    public void handleNewReport(Report report) {
	controller.newReport(report);
    }

    /**
     * Handles new chat messages.
     */
    public void handleNewChatMessage(ChatMessage message) {
	frame.ensureChat();
	frame.chatFrame.newMessage(message);
    }

    /**
     * Install any generic issue handlers for issues it can
     * handle automatically.
     */ 
    protected void addIdeelIssueHandlers() {

	controller.addIssueHandler
	    ("report",
	     // Want to be able to say "report to <sender id>" /\/
	     new IdeelIssueHandler("send report") {
		public void handleIssue(Issue i) {

		    // Syntax is: (report ?destination &rest contents)

		    // Determine the intended destination.
		    LList parameters = (LList)i.getParameters();
		    String destination = parameters.car().toString();

		    // Construct the report.
		    String text = Lisp.elementsToString(parameters.cdr());
		    Report report = new Report(text);
		    report.setSenderId((String)getAgentIPCName());

		    // Send the report.
		    Debug.noteln("Sending to " + destination + " " + text);
		    IPC.sendObject(destination, report);

		    // Mark issue as complete.
		    i.setStatus(STATUS_COMPLETE);
		}
	    });

    }

    /*
     * Some useful handler classes
     */

    public static class ForwardingIssueHandler extends IdeelIssueHandler {

	protected String action;
	protected String toName;
	protected boolean reportBack;

	public ForwardingIssueHandler(String action,
				      String toName,
				      boolean reportBack) {
	    super(action + " to " + toName); // action description
	    this.action = action;
	    this.toName = toName;
	    this.reportBack = reportBack;
	}

	public void handleIssue(Issue issue) {
	    IdeelIssue i = (IdeelIssue)issue;
	    i.forwardIssue(toName, reportBack);
	    i.setStatus(reportBack ? STATUS_EXECUTING : STATUS_COMPLETE);
	}

	public boolean appliesTo(IdeelIssue issue) {
	    return true;
	}

	public void addIssueOptions(IdeelIssue issue) {
	    issue.addOption
		(new IdeelController.AutomaticOption
		 (issue, this, getActionDescription()));
	}

    }

    public static class EscalateHandler extends ForwardingIssueHandler {
	public EscalateHandler(String supervisor) {
	    super("escalate", supervisor, false);
	}
    }

    public static class DelegateHandler extends ForwardingIssueHandler {
	public DelegateHandler(String subordinate) {
	    super("delegate", subordinate, true);
	}
    }


}

// Issues:
// * Should the class name be BasicIdeel or just Ideel?  So long as the
//   class I_DEEL exists, BasicIdeel will be less confusing / error-prone.
