package org.jcon.df.edit.widget;

import org.jcon.df.edit.control.BooleanControl;
import org.jcon.df.edit.control.Control;
import org.jcon.df.edit.control.ControlAction;
import org.jcon.df.edit.module.Module;
import org.jcon.ui.CursorChanger;
import org.jcon.util.DataLib;
import java.awt.Button;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.TextField;
import java.awt.event.ActionListener;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.swing.JTextField;

/**
* This class contains a set of "widgets" which are Controls
* and Modules. It allows easy manipulation of these. It is
* designed to allow a CONTROLLER to easily control a VIEW.
* Duplicate names are not permitted. Some convenience
* methods are supplied. Additional manipulation can be
* done with getControl() or getModule(), and casting.
* The WidgetSetEvent may also be subscribed to.
*
* @author Jack Harich 
*/
public class WidgetSet {

//---------- Private Fields ------------------------------
//----- Properties
private WidgetSetView widgetSetView;
private boolean       isTaskDelay;

// Key = String name, Object = Control
private Hashtable controls = new Hashtable();

// Key = String name, Object = Module
private Hashtable modules = new Hashtable();

//----- Internal
private Vector        listeners = new Vector();
private CursorChanger cursorChanger = new CursorChanger();

//---------- Properties ----------------------------------
//----- widgetSetView
// We should not expose this, plus this introduces coupling
// to an unrelated class and makes this less reusable ***
// Good would be interface WidgetSetView. <-----<<<
public void setWidgetSetView(WidgetSetView widgetSetView) {
    this.widgetSetView = widgetSetView;
}
// *** Disabled 1/29/99 by JH, added interface WidgetSetView.
// A search shows this method is not used, but this may be wrong.
//public DataView getDataView() {
//    return dataView;
//}
//----- taskDelay
/**
* Sets the TaskDelay property, which indicates if a task
* is currently delayed. If true then the window is disabled
* and an hourglass is shown. If false then the window and
* mouse icon are normal.
* <p>
* This method is designed to be called after a button click
* or such, with true, and then again after the task delay
* is over, with false. For robustness call setTaskDelay(false)
* in a finally block.
*/
public void setTaskDelay(boolean isTaskDelay) {
    this.isTaskDelay = isTaskDelay;
    if (isTaskDelay) {
        cursorChanger.beginWaitCursor(widgetSetView.getWindow());
    } else {
        cursorChanger.endWaitCursor();
    }
}
public boolean isTaskDelay() {
    return isTaskDelay;
}
//----- Other
public void setControls(Vector list) {
    controls.clear();
    addControls(list);
}
public void setModules(Vector list) {
    modules.clear();
    addModules(list);
}
public String getViewName() {
    return widgetSetView.getViewName();
}
//---------- Events --------------------------------------
public void addWidgetSetListener(WidgetSetListener listener) {
    listeners.addElement(listener);
}
public void removeWidgetSetListener(WidgetSetListener listener) {
    listeners.removeElement(listener);
}
// public to allow use by widgetSetView, etc
public void fireEvent(WidgetSetEvent evt) {
    if (listeners.isEmpty()) return;
    
    Vector list;
    synchronized(this) {
        list = (Vector)listeners.clone();
    }
    for (int i = 0; i < list.size(); i++) {
        WidgetSetListener listener =
            (WidgetSetListener)list.elementAt(i);
        listener.processWidgetSetEvent(evt);
    }
}
//---------- Public Methods ------------------------------
/**
* Adds the widgets in the topWidget to the existing ones.
* This should only be done AFTER setControls() and
* setWidgets(). A "top widget" is at the top of the
* containment heirarchy.
* RECURSIVE.
*/
public void addTopWidget(Widget topWidget) {
    if (topWidget instanceof Control) {
        addControl((Control)topWidget);
    } else {
        Module module = (Module)topWidget;
        addModules(module.getChildModules()); 
        addControls(module.getControls());
        // Add all module decendents
        Vector list = module.getChildModules();
        for (int i = list.size(); --i >= 0;) {
            Module childModule = (Module)list.elementAt(i);
            addTopWidget(childModule); // RECURSE
        }        
    }
}
/**
* Removes the topWidget and all its contained widgets.
*/
public void removeTopWidget(Widget topWidget) {
    // *************** do
    print(".removeTopWidget() - Not yet implemented");
}
//----- Get a specific widget
public Control getControl(String name) {
    Control control = (Control)controls.get(name);
    if (control == null) {
        // Show developer all my control names to help solve the problem
        print(".getControl() ---- Control name '" + name + "' not found, My control names are: ----\n" + 
            listControlNames("\n", true));
        throw new IllegalArgumentException(
            "Control name '" + name + "' not found.");
    }
    return control;
}
public Module getModule(String name) {
    Module module = (Module)modules.get(name);
    if (module == null) throw new IllegalArgumentException(
        "Module name '" + name + "' not found.");
    return module;
}
//----- ControlValue, called frequently
public void setControlValue(String name, Object value) {
    getControl(name).setValue(value);
}
public Object getControlValue(String name) {
    return getControl(name).getValue();
}
// Boolean variation. for BooleamControl
public void setControlValue(String name, boolean isTrue) {
    String value = (isTrue ? "true" : "false");
    getControl(name).setValue(value);
}
public boolean isControlValueTrue(String name) {
    Object value = getControl(name).getValue();
    return (value.equals("1") ? true : false);
}
//----- Control isSelected
//----- isSelected
public void setControlSelected(String name, boolean isSelected) {
    BooleanControl control = (BooleanControl)getControl(name);
    if (control.isSelected() != isSelected) {
        control.setSelected(isSelected);
    }
}
public boolean isControlSelected(String name) {
    BooleanControl control = (BooleanControl)getControl(name);
    return control.isSelected();    
}
//---------- Controls
//----- Enabled
public void setControlEnabled(String name, boolean enabled) {
    Component comp = getControl(name).getComponent();
    if (comp.isEnabled() != enabled) comp.setEnabled(enabled);
}
public boolean isControlEnabled(String name) {
    return getControl(name).getComponent().isEnabled();
}
//----- Editable - *** Add more as needed
public void setControlEditable(String name, boolean editable) {
    Component comp = getControl(name).getComponent();
    if (comp instanceof TextField) {
        TextField field = (TextField)comp;
        if (field.isEditable() != editable) {
            field.setEditable(editable);
        }
    } else if (comp instanceof JTextField) {
        JTextField field = (JTextField)comp;
        if (field.isEditable() != editable) {
            field.setEditable(editable);
        }

		// Set the background to gray or white, to be 
		// compatible with TextField
		// Try removing this with swing 1.2 beta - CMC
		// Note that this also does not consider ColumDefs
		if (editable) {
			field.setBackground(Color.white);
			field.repaint();
		}
		else {
			field.setBackground(Color.lightGray);
			field.repaint();
		}
    } else {
        print(".setControlEditable() - " + name +
             " is not a TextField");
    }
}
//----- Other
public void doClickControlAction(String name) {
    ControlAction action = (ControlAction)getControl(name);
    action.doClick();
}
public void requestControlFocus(String name) {
    getControl(name).getComponent().requestFocus();
}
public void setViewTitle(String title) {
    widgetSetView.setTitle(title);
}
public Enumeration getControlNames() {
    return controls.elements();
}
public boolean hasControl(String name) {
	return controls.containsKey(name);
}
// joshy
public String findHelpId(Component comp) {
    Enumeration en = controls.elements();
    while(en.hasMoreElements()) {
      Control c = (Control)en.nextElement();
      //      print("looking at component: " + comp);
      if(c.getComponent() == comp) {
        //    print("found component = " + comp);
        return c.getHelpId();
      }
    }
    return null;
}
public String listControlNames(String delimiter, boolean isSorted) {
    return DataLib.convertEnumerationToString(
        controls.keys(), delimiter, isSorted);
}
//---------- Private Methods -----------------------------
private void addControls(Vector list) {
    for (int i = list.size(); --i >= 0;) {
        Control control = (Control)list.elementAt(i);
        addControl(control);
    }
}
private void addModules(Vector list) {
    for (int i = list.size(); --i >= 0;) {
        Module module = (Module)list.elementAt(i);
        addModule(module);
    }
}
private void addControl(Control control) {
    Object old = controls.put(control.getName(), control);
    if (old != null) throw new IllegalStateException(
        "The control name '" + control.getName() + "' is a duplicate.");
}
private void addModule(Module module) {
    Object old = modules.put(module.getName(), module);
    if (old != null) throw new IllegalStateException(
        "The module name '" + module.getName() + "' is a duplicate.");
}
//--- Std
private static void print(String text) {
    System.out.println("WidgetSet" + text);
}

} // End class
