/********************************************************************
 * Copyright © 2004 EDS. All rights reserved
 ********************************************************************/
package gov.vha.edb.rules.web.builder;

import ilog.rules.repository.model.IlrRepException;
import ilog.rules.repository.model.IlrRepRuntimeException;
import ilog.rules.util.logging.Level;
import ilog.rules.webui.IlrWUtils;
import ilog.webui.dhtml.IlxWComponent;
import ilog.webui.dhtml.IlxWPort;
import ilog.webui.dhtml.IlxWUtil;
import ilog.webui.dhtml.components.*;

import javax.jmi.reflect.JmiException;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

public abstract class WebAction implements Serializable {

  private static final long serialVersionUID = 6654382296065793959L;
  private boolean enabled = false;
  private boolean selected = false;
  private String name;
  private String displayName;
  private String icon;
  private String functionName;
  private PropertyChangeSupport changeSupport = null;

    public WebAction(String name,
                     String displayName,
                     String functionName,
                     String icon) {
      this.name = name;
      this.displayName = displayName;
      this.functionName = functionName;
      this.icon = icon;
    }

  public WebAction(String name,
                   String displayName,
                   String functionName) {
    this(name, displayName, functionName, null);
  }

  public WebAction(String name,
                   String displayName) {
    this(name, displayName, null, null);
  }

  public WebAction(String name) {
    this.name = name;
    this.displayName = WebBuilder.getProperty(name+".text");
    this.functionName = WebBuilder.getProperty(name+".function");
    String icon = WebBuilder.getProperty(name+".icon");
    if (icon!=null) {
      this.icon = IlrWUtils.getIconResourceName(icon);
    }
  }

  public void setDisplayName(String name) {
    String oldValue = this.displayName;
    if (!name.equals(oldValue)) {
      this.displayName = name;
      firePropertyChange("displayName", oldValue, name);
    }
  }

  public void setIcon(String icon) {
    String oldValue = this.icon;
    if (!icon.equals(oldValue)) {
      this.icon = icon;
      firePropertyChange("icon", oldValue, icon);
    }
  }

  public void setEnabled(boolean newValue) {
    boolean oldValue = this.enabled;
    if (newValue!= oldValue) {
      this.enabled = newValue;
      firePropertyChange("enabled",
                         new Boolean(oldValue),
                         new Boolean(newValue));
    }
  }

  public boolean isEnabled() {
    return enabled;
  }

  public boolean isEnabled(WebBuilder builder) {
    return true;
  }

  public boolean isSelectable() {
    return false;
  }

  public boolean isInvokable() {
    return true;
  }

  public void setSelected(boolean newValue) {
    boolean oldValue = this.selected;
    if (newValue!= oldValue) {
      this.selected = newValue;
      firePropertyChange("selected",
                         new Boolean(oldValue),
                         new Boolean(newValue));
    }
  }

  public boolean isSelected() {
    return selected;
  }

  public boolean isSelected(WebBuilder builder) {
    return false;
  }

  public String getName() {
    return name;
  }

  public String getDisplayName() {
    return displayName;
  }

  public String getIcon() {
    return icon;
  }

  public String getFunctionName() {
    return functionName;
  }

  protected void firePropertyChange(String propertyName,
                                    Object oldValue,
                                    Object newValue) {
    if (changeSupport != null) {
      changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

  public synchronized void addPropertyChangeListener(PropertyChangeListener
                                                     listener) {
    if (changeSupport == null) {
      changeSupport = new PropertyChangeSupport(this);
    }
    changeSupport.addPropertyChangeListener(listener);
  }

  public synchronized void removePropertyChangeListener(PropertyChangeListener
                                                        listener) {
    if (changeSupport != null) {
      changeSupport.removePropertyChangeListener(listener);
    }
  }

  public abstract void invoke(IlxWPort port, String [] args) throws Throwable;

  public void doInvoke(IlxWPort port, String [] args) {
    if (isInvokable()) {
      try {
        invoke(port, args);
      }
      catch (IlrRepException ex){
      	WebBuilder builder
          = WebBuilder.getInstance(port.getRequest());
      	Throwable t = getFirstCause(ex);
      	builder.addAlert(t.getMessage());
      	builder.getLogger().log(Level.FINEST, "Error during action " +
                              	getName(), ex);
      }
      catch (JmiException ex) {
        WebBuilder builder
          = WebBuilder.getInstance(port.getRequest());
        Throwable t = getFirstCause(ex);
        builder.addAlert(t.getMessage());
        builder.getLogger().log(Level.FINEST, "Error during action " +
                                getName(), ex);
      } 
      catch (Throwable ex) {
        Throwable t = getFirstCause(ex);
        WebBuilder builder
          = WebBuilder.getInstance(port.getRequest());
        builder.getLogger().log(Level.FINE, "Editor.UncaughtError", t);
        String msg = "Internal Error: " + t.getClass().getName() +
          ": " + t.getMessage();
        t.printStackTrace();
        builder.addAlert(msg);
      }
    }
  }

  public static Throwable getFirstCause(Throwable thrown) {
    if (thrown==null)
      return null;
    Throwable res = null;
    if (thrown instanceof InvocationTargetException) {
      InvocationTargetException ex = (InvocationTargetException)thrown;
      res = ex.getTargetException();
    }
    else if (thrown instanceof IlrRepException) {
      IlrRepException ex = (IlrRepException)thrown;
      res = ex.getRootCause();
    }
    else if (thrown instanceof IlrRepRuntimeException) {
      IlrRepRuntimeException ex = (IlrRepRuntimeException)thrown;
      res = ex.getRootCause();
    }
    if (res!=null)
      return getFirstCause(res);
    return thrown;
  }

  public IlxWAction makeAction() {
    IlxWAction action;
    if (functionName!=null) {
      String fun
        = "new Function(\"(new WebAction(\\\""+IlxWUtil.escape(name)
        +"\\\")).call("
        + IlxWUtil.escape(functionName) + ")\")";
      action = new IlxWJSAction(fun);
    } else {
      action = new IlxWJavaAction() {
          private static final long serialVersionUID = 0L;

        public void perform(IlxWPort port,
                              IlxWComponent source,
                              Object [] args) {
            WebAction.this.doInvoke(port, new String[0]);
          }
        };
    }
    return action;
  }

  private void configureMenuItem(IlxWMenuItem item) {
    item.setAction(makeAction());
    addPropertyChangeListener(new PropertyChangeHandler(item));
    if (icon!=null) {
      item.setIcon(icon);
    }
  }

  private static class PropertyChangeHandler
    implements PropertyChangeListener, Serializable {
    private static final long serialVersionUID = -49248585466350684L;
    private IlxWMenuItem item;
    private PropertyChangeHandler(IlxWMenuItem item) {
      this.item = item;
    }
    public void propertyChange(PropertyChangeEvent event) {
      String name = event.getPropertyName();
      if ("enabled".equals(name)) {
        boolean val = ((Boolean)event.getNewValue()).booleanValue();
        item.setEnabled(val);
      } else if ("selected".equals(name)) {
        boolean val = ((Boolean)event.getNewValue()).booleanValue();
        ((IlxWCheckBoxMenuItem)item).setSelected(val);
      } else if ("displayName".equals(name)) {
        item.setText((String)event.getNewValue());
      } else if ("icon".equals(name)) {
        item.setIcon((String)event.getNewValue());
      }
    }
  }

  public IlxWMenuItem makeMenuItem(WebBuilder builder) {
    return makeMenuItem(builder.getMessage(this.displayName.trim()));
  }

  public IlxWMenuItem makeMenuItem(String theDisplayName) {
    IlxWMenuItem item;
    if (isSelectable()) {
      item = new IlxWCheckBoxMenuItem(theDisplayName);
      ((IlxWCheckBoxMenuItem)item).setSelected(selected);
    } else if (isInvokable()) {
      item = new IlxWMenuItem(theDisplayName);
    } else {
      item = new IlxWMenu(theDisplayName);
    }
    item.setEnabled(enabled);
    configureMenuItem(item);
    return item;
  }

  public static class Map implements Serializable {

    private static final long serialVersionUID = 4391738145320209430L;
    private HashMap map = new HashMap();
    private IlxWActionHolder ah
      = new IlxWActionHolder(new IlxWJavaAction() {
          private static final long serialVersionUID = 0L;

        public void perform(IlxWPort port,
                              IlxWComponent source,
                              Object [] args) {
            String name = (String)args[0];
            WebAction action = getWebAction(name);
            String [] sargs = new String[args.length-1];
            for (int i=0; i<sargs.length; i++) {
              sargs[i] = (String)args[i+1];
            }
            action.doInvoke(port, sargs);
          }
        });

    public void registerWebAction(WebAction action) {
      map.put(action.getName(), action);
    }

    public WebAction getWebAction(String name) {
      return (WebAction)map.get(name);
    }

    /**
     * <p>Returns all the actions registered in the map.</p>
     */
    public Iterator getWebActions() {
      return map.values().iterator();
    }

    /**
     * <p>Returns an array containing all <code>WebActions</code> starting with
     * the given name.</p>
     */
    public Iterator getWebActions(String name) {
      // Not very efficient as you browse the whole map.
      Iterator entries = map.entrySet().iterator();
      ArrayList al = new ArrayList();
      while (entries.hasNext()) {
        java.util.Map.Entry entry  = (java.util.Map.Entry)entries.next();
        String key = (String)entry.getKey();
        if (key.startsWith(name)) {
          al.add(entry.getValue());
        }
      }
      return al.iterator();
    }

    public void removeWebActions(String name) {
      Iterator entries = map.entrySet().iterator();
      while (entries.hasNext()) {
        java.util.Map.Entry entry  = (java.util.Map.Entry)entries.next();
        String key = (String)entry.getKey();
        if (key.startsWith(name)) {
          entries.remove();
        }
      }
    }

    public IlxWActionHolder getActionHolder() {
      return ah;
    }

  }

}
