/* Author: Jeff Dalton <J.Dalton@ed.ac.uk>
 * Updated: Thu Jun  7 16:22:40 2001 by Jeff Dalton
 * Copyright: (c) 2001, AIAI, University of Edinburgh
 */

package ix.ileed;

import javax.swing.*;
import javax.swing.table.*;
import java.awt.Font;
import java.awt.Dimension;
import java.awt.event.*;
import java.util.*;

import ix.ideel.IssueViewingTable;

import ix.iface.util.GridColumn;

import ix.icore.process.StatusValues;

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

/** 
 * A simple process / product viewer
 */

public class SimpleProcessViewer extends JPanel implements ProcessViewer {

    // JPanel layout manager defaults to FlowLayout.

    ProcessModel model;

    ProcessTable processTable;
    ProductTable productTable;

    LList processExpansionTree;
    Vector products;

    public SimpleProcessViewer() {
	setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    }

    public void setProcessModel(ProcessModel model) {
	// We can't set things up in any interesting way until after
	// there's a process model we can ask for the relevant information.
	this.model = model;
	setUpTables();
    }

    public synchronized void statusUpdate(final ProcessStatusUpdate up) {
	if (SwingUtilities.isEventDispatchThread()) {
	    // Happens when there's a "reset" invoked via the interface.
	    Debug.noteln("Process statusUpdate in event dispatch thread");
	}
	SwingUtilities.invokeLater(new Runnable() {
	    public void run() {
		processTable.statusUpdate(up);
		productTable.statusUpdate(up);
	    }
	});
    }

    public synchronized void reset() {
	processTable.reset();
	productTable.reset();
    }

    void setUpTables() {
	processExpansionTree = model.staticExpansionTree();
	products = model.getAllProducts();

	processTable = new ProcessTable(processExpansionTree);
	productTable = new ProductTable(products);

	// add(processTable);
	JPanel processPanel = new JPanel();
	processPanel.add(processTable);
	JScrollPane processScroll = new JScrollPane(processPanel);
	processScroll.setBorder
	    (BorderFactory.createTitledBorder("Process status"));
	add(processScroll);

	// add(productTable);
	JPanel productPanel = new JPanel();
	productPanel.setBorder
	    (BorderFactory.createTitledBorder("Product status"));
	productPanel.add(productTable);
	add(productPanel);

    }

    static class ProcessTable extends StatusTable {

	JLabel actionLabel = new JLabel("Activity");
	JLabel statusLabel = new JLabel("Status");

	List rows = new ArrayList();

	ProcessTable(LList expansionTree) {
	    super();
	    vectorizeExpansionTree(expansionTree, 0);
	    fillTable();
	}

	// The expansion tree shows the action nesting present in the plan.
	// The tree is a list of entries of the form (pattern subtree).

	void vectorizeExpansionTree(LList tree, int depth) {
	    for (LList at = tree; at != Lisp.NIL; at = at.cdr()) {
		LList e = (LList)at.car();
		Object pattern = (Object)e.elementAt(0);
		LList subtree = (LList)e.elementAt(1);
		String name = pattern.toString();

		JTextField tf = new JTextField
		    (Util.repeatString("     ", depth) + name,
		     40);
		// Bold text if there's a subtree.
		if (subtree != Lisp.NIL)
		    tf.setFont(tf.getFont().deriveFont(Font.BOLD));

		JTextField sf = makeStatusField(name);

		Row row = new Row(tf, sf);
		row.subtree = subtree;
		row.depth = depth;
		row.open = true; // subtree == Lisp.NIL;
		tf.addMouseListener(makeMouseListener(row));
		rows.add(row);
		vectorizeExpansionTree(subtree, depth+1);
	    }
	}

	void clearTable() {
	    nameCol.removeAll();
	    statusCol.removeAll();
	}

	void fillTable() {
	    nameCol.add(actionLabel);
	    statusCol.add(statusLabel);
	    for (ListIterator i = rows.listIterator(); i.hasNext();) {
		Row row = (Row)i.next();
		nameCol.add(row.text);
		statusCol.add(row.status);
		if (!row.open) {
		    while(i.hasNext()) {
			Row below = (Row)i.next();
			if (below.depth <= row.depth) {
			    i.previous();
			    break;
			}
		    }
		}
	    }
	}

	static class Row {
	    LList subtree;
	    int depth;
	    boolean open;
	    JTextField text;
	    JTextField status;
	    Row(JTextField text, JTextField status) {
		this.text = text;
		this.status = status;
	    }
	}

	/**
	 * Returns a listener than can be called when the user clicks
	 * in the text of an action description.
	 */
	MouseListener makeMouseListener(final Row row) {
	    return new MouseAdapter() {
		public void mouseClicked(MouseEvent e) {
		    JTextField clicked = (JTextField)e.getComponent();

		    String selectedIssueText = clicked.getText();
		    Debug.noteln("User clicked in", selectedIssueText);

		    if (row.subtree != Lisp.NIL) {
			row.open = !row.open;
			clearTable();
			fillTable();
			invalidate();
			SwingUtilities.getRoot(ProcessTable.this).validate();
		    }
		    
		}
	    };
	}

	void statusUpdate(ProcessStatusUpdate up) {
	    // Node status changes.
	    for (Enumeration ce = up.statusChanges.elements();
		 ce.hasMoreElements();) {
		StatusChange change = (StatusChange)ce.nextElement();
		setStatus(change.action, change.status);
	    }
	}

    }

    static class ProductTable extends StatusTable {

	ProductTable(Vector products) {
	    super();
	    nameCol.add(new JLabel("Product"));
	    statusCol.add(new JLabel("Status"));
	    for (Iterator pi = products.iterator(); pi.hasNext();) {
		Product p = (Product)pi.next();
		nameCol.add(new JTextField(p.name, 20));
		addStatus(p.name);
	    }
	}

	void statusUpdate(ProcessStatusUpdate up) {

	    for (Iterator pi = up.products.iterator(); pi.hasNext();) {
		Product p = (Product)pi.next();
		setStatus(p.name, p.status);
	    }
	}
	
    }

    static class StatusTable extends JPanel {

	GridColumn nameCol = new GridColumn();
	GridColumn statusCol = new GridColumn();

	HashMap statMap = new HashMap();

	StatusTable() {
	    super();
	    setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
	    add(nameCol);
	    add(statusCol);
	}

	void addStatus(Object item) {
	    statusCol.add(makeStatusField(item));
	}

	JTextField makeStatusField(Object item) {
	    JTextField statField = new JTextField("     ", 5);
	    statMap.put(item, statField);
	    return statField;
	}

	void setStatus(Object item, int status) {
	    JTextField tf = (JTextField)statMap.get(item);
	    Debug.assert(tf != null, "Can't find item for status");
	    tf.setBackground(ViewColor.statusColor[status]);
	}

	void reset() {
	    for (Iterator ki = statMap.keySet().iterator(); ki.hasNext();) {
		Object key = ki.next();
		setStatus(key, StatusValues.STATUS_BLANK);
	    }
	}

    }

}

// Issues:
// * Should it extend JPanel or contain one?
