package brn.gui.views;

import java.util.ArrayList;

import jist.runtime.JistAPI;
import jist.swans.Constants;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;

import brn.gui.Activator;
import brn.gui.BrnGuiConstants;
import brn.gui.datasource.Provider;
import brn.gui.datasource.Source;

/**
 * This sample class demonstrates how to plug-in a new workbench view. The view
 * shows data obtained from the model. The sample creates a dummy model on the
 * fly, but a real implementation would connect to the model available either in
 * this or another plug-in (e.g. the workspace). The view is connected to the
 * model using a content provider.
 * <p>
 * The view uses a label provider to define how model objects should be
 * presented in the view. Each view can present the same model objects using
 * different labels and icons, if needed. Alternatively, a single label provider
 * can be shared between views in order to ensure that objects of the same type
 * are presented in the same way everywhere.
 * <p>
 */

public class ConfigView extends ViewPart implements IPropertyChangeListener {

  public final static String ID = "brn.gui.views.ConfigView";

  private TreeViewer viewer;

  private Provider provider; // @jve:decl-index=0:

  private EditableTreeObject treeObjSource;

  private TreeObjectSimulation treeObjSimulator;

  private TreeObjectTime treeObjTime;

  private EditableTreeObject treeObjDbHost;

  private EditableTreeObject treeObjDbRes;

  private EditableTreeObject treeObjDbUser;

  private EditableTreeObject treeObjDbPassword;

  private EditableTreeObject treeObjDbJobId;

  //  private TreeObjectParameters treeObjParameters;

  /*
   * The content provider class is responsible for providing objects to the
   * view. It can wrap existing objects in adapters or simply return objects
   * as-is. These objects may be sensitive to the current input of the view, or
   * ignore it and always show the same content (like Task List, for example).
   */

  class TreeObject implements IAdaptable {
    private String name;
    private String value;

    private TreeParent parent;

    public TreeObject(String name) {
      this.name = name;
    }

    public String getName() {
      return name;
    }

    public void setParent(TreeParent parent) {
      this.parent = parent;
    }

    public TreeParent getParent() {
      return parent;
    }

    public String toString() {
      return getName();
    }

    public Object getAdapter(Class key) {
      return null;
    }

    public String getValue() {
      return value;
    }

    public void setValue(String value) {
      this.value = value;
    }

    public boolean canModify() {
      return false;
    }

    public String[] getChoices() {
      return new String[] {};
    }
  }

  class TreeParent extends TreeObject {
    private ArrayList<TreeObject> children;

    public TreeParent(String name) {
      super(name);
      children = new ArrayList<TreeObject>();
    }

    public void addChild(TreeObject child) {
      children.add(child);
      child.setParent(this);
    }

    public void removeChild(TreeObject child) {
      children.remove(child);
      child.setParent(null);
    }

    public TreeObject[] getChildren() {
      return (TreeObject[]) children.toArray(new TreeObject[children.size()]);
    }

    public boolean hasChildren() {
      return children.size() > 0;
    }
  }

  class TreeObjectSimulation extends TreeObject {

    public TreeObjectSimulation(String name) {
      super(name);
      update();
    }

    public void update() {
      // Set simulator string
      if (null != provider.getDataSource()
          &&provider.getDataSource().isConnected())
        setValue(provider.getDataSource().toString());
      else
        setValue("not connected");
    }
  }

  class TreeObjectTime extends TreeObject {

    public TreeObjectTime(String name) {
      super(name);
      update();
    }

    public void update() {
      // Set time
      if (null == provider.getDataSource()
          ||!provider.getDataSource().isConnected())
        setValue("");
      else {
        if (!provider.getDataSource().isPaused())
          setValue("running");
        else {
          long time = provider.getDataSource().getTime();
          if (JistAPI.END == time)
            setValue("end");
          else
            setValue(String.valueOf(time/(double)Constants.MILLI_SECOND) + " ms");
        }
      }
    }

  }

//  class TreeObjectParameters extends TreeParent implements IAdaptable {
//
//    protected IPropertySource paramElPs;
//
//    public TreeObjectParameters(String name) {
//      super(name);
//      update();
//    }
//
//    public void update() {
//      // Set config
//      if (null != provider.getDataSource()
//          &&provider.getDataSource().isConnected()) {
//        try {
//          AbstractParams config = (AbstractParams)
//              provider.getDataSource().getContribution(
//                  provider.getDataSource().getConfigId());
//          setConfig(config);
//        } catch (CoreException e) {
//          Activator.log(e);
//          clear();
//        } catch (Exception e) {
//          Activator.getDefault().logError("Error during introspection.", e);
//          clear();
//        }
//      }
//      else {
//        clear();
//      }
//    }
//
//    public void setConfig(AbstractParams config) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, IntrospectionException {
//      BeanInfo info = Introspector.getBeanInfo(config.getClass());
//      BeanDescriptor descr = info.getBeanDescriptor();
//      setValue(descr.getName());
//      PropertyDescriptor[] props = info.getPropertyDescriptors();
//
//      Vector<PropertyDescriptor> vec = new Vector<PropertyDescriptor>(props.length);
//      for (int k=0; k<props.length; k++) {
//        String name = props[k].getDisplayName();
//        if (name.equals("path") || name.equals("type") || name.equals("content"))
//          continue;
//        boolean added = false;
//        for (int i=0; i<vec.size(); i++) {
//          String str = ((PropertyDescriptor)vec.elementAt(i)).
//            getDisplayName();
//          if (name.compareToIgnoreCase(str) < 0) {
//            vec.insertElementAt(props[k], i);
//            added = true;
//            break;
//          }
//        }
//        if (!added)
//          vec.addElement(props[k]);
//      }
//
//      String[][] m_properties = new String[vec.size()][2];
//      for (int k=0; k<vec.size(); k++) {
//        PropertyDescriptor prop =
//          (PropertyDescriptor)vec.elementAt(k);
//        m_properties[k][0] = prop.getDisplayName();
//        Method mRead = prop.getReadMethod();
//        if (mRead != null &&
//         mRead.getParameterTypes().length == 0) {
//          Object value = mRead.invoke(config, (Object[])null);
//          m_properties[k][1] = objToString(value);
//        }
//        else
//          m_properties[k][1] = "error";
//
//        TreeObject treeObj = new TreeObject(m_properties[k][0]);
//        treeObj.setValue(m_properties[k][1]);
//        addChild(treeObj);
//      }
//    }
//
//    public void clear() {
//      setValue("");
//
//      TreeObject[] children = getChildren();
//      for (int i = 0; i < children.length; i++)
//        removeChild(children[i]);
//    }
//
//    private String objToString(Object value) {
//      if (value==null)
//        return "null";
//      if (value instanceof Dimension) {
//        Dimension dim = (Dimension)value;
//        return ""+dim.width+","+dim.height;
//      }
//      else if (value instanceof Insets) {
//        Insets ins = (Insets)value;
//        return ""+ins.left+","+ins.top+","+ins.right+","+ins.bottom;
//      }
//      else if (value instanceof Rectangle) {
//        Rectangle rc = (Rectangle)value;
//        return ""+rc.x+","+rc.y+","+rc.width+","+rc.height;
//      }
//      else if (value instanceof Color) {
//        Color col = (Color)value;
//        return ""+col.getRed()+","+col.getGreen()+","+col.getBlue();
//      }
//      return value.toString();
//    }
//
//    /* (non-Javadoc)
//     * @see brn.gui.views.ConfigView.TreeObject#getAdapter(java.lang.Class)
//     */
//    @Override
//    public Object getAdapter(Class adapter) {
//      if (adapter == IPropertySource.class) {
//        if (paramElPs == null) {
//          // cache the buttonelementpropertysource
//          paramElPs = new ConfigPropertySource(provider.getDataSource());
//        }
//        return paramElPs;
//      }
//      return super.getAdapter(adapter);
//    }
//  }

  class EditableTreeObject extends TreeObject {
    private String[] choises;
    private boolean readonly;

    public EditableTreeObject(String name, String[] choises, boolean readonly) {
      super(name);
      this.choises = choises;
      this.readonly = readonly;

      IPreferenceStore store = Activator.getDefault().getPreferenceStore();
      super.setValue(store.getString(getName()));
    }

    /* (non-Javadoc)
     * @see brn.gui.views.ConfigView.TreeObject#canModify()
     */
    @Override
    public boolean canModify() {
      return true;
    }

    /* (non-Javadoc)
     * @see brn.gui.views.ConfigView.TreeObject#getChoices()
     */
    @Override
    public String[] getChoices() {
      return choises;
    }

    /* (non-Javadoc)
     * @see brn.gui.views.ConfigView.TreeObject#setValue(java.lang.String)
     */
    @Override
    public void setValue(String value) {
      if (readonly) {
        for (int i = 0; i < choises.length; i++) {
          if (value.equals(choises[i]))
            super.setValue(value);
        }
      } else {
        super.setValue(value);
      }

      IPreferenceStore store = Activator.getDefault().getPreferenceStore();
      store.setValue(getName(), getValue());
    }
  }

//  class PreferenceSaveHook {
//    public void run(TreeObject object) {
//      IPreferenceStore store = Activator.getDefault().getPreferenceStore();
//      if (object.getName().equals(BrnGuiConstants.PREF_DB_JOBID)) {
//        try {
//          int value = Integer.valueOf(object.getValue());
//          store.setValue(object.getName(), value);
//        } catch (NumberFormatException e) {
//          object.setValue(value)
//        }
//      } else {
//        store.setValue(object.getName(), object.getValue());
//      }
//    }
//  }

  class ViewContentProvider implements IStructuredContentProvider,
      ITreeContentProvider {
    private TreeParent invisibleRoot;

    public void inputChanged(Viewer v, Object oldInput, Object newInput) {
    }

    public void dispose() {
    }

    public Object[] getElements(Object parent) {
      IViewSite viewSite = getViewSite();
      if (parent.equals(viewSite)) {
        if (invisibleRoot == null)
          initialize();
        return getChildren(invisibleRoot);
      }
      return getChildren(parent);
    }

    public Object getParent(Object child) {
      if (child instanceof TreeObject) {
        return ((TreeObject) child).getParent();
      }
      return null;
    }

    public Object[] getChildren(Object parent) {
      if (parent instanceof TreeParent) {
        return ((TreeParent) parent).getChildren();
      }
      return new Object[0];
    }

    public boolean hasChildren(Object parent) {
      if (parent instanceof TreeParent)
        return ((TreeParent) parent).hasChildren();
      return false;
    }

    /*
     * We will set up a dummy model to initialize tree heararchy. In a real
     * code, you will connect to a real model and expose its hierarchy.
     */
    private void initialize() {
      treeObjSource = new EditableTreeObject(BrnGuiConstants.PREF_DATA_SOURCE,
          new String[] {BrnGuiConstants.PREF_DATA_SOURCE_SIM,
          BrnGuiConstants.PREF_DATA_SOURCE_DB}, true);
      treeObjSimulator = new TreeObjectSimulation("Simulator");
      treeObjTime = new TreeObjectTime("Time");
//      treeObjParameters = new TreeObjectParameters("Parameters");
      treeObjDbHost = new EditableTreeObject(BrnGuiConstants.PREF_DB_HOST,
          new String[] {"localhost", "gorillaz", "brn-suse093-2"}, false);
      treeObjDbRes = new EditableTreeObject(BrnGuiConstants.PREF_DATABASE,
          new String[] {"simulation"}, false);
      treeObjDbUser = new EditableTreeObject(BrnGuiConstants.PREF_DB_USER,
          new String[] {"distsim"}, false);
      treeObjDbPassword = new EditableTreeObject(BrnGuiConstants.PREF_DB_PASSWORD,
          new String[] {"distsim"}, false);
      treeObjDbJobId = new EditableTreeObject(BrnGuiConstants.PREF_DB_JOBID,
          new String[] {"1"}, false);


      invisibleRoot = new TreeParent("");
      invisibleRoot.addChild(treeObjSource);
      invisibleRoot.addChild(treeObjSimulator);
      invisibleRoot.addChild(treeObjTime);
//      invisibleRoot.addChild(treeObjParameters);
      invisibleRoot.addChild(treeObjDbRes);
      invisibleRoot.addChild(treeObjDbHost);
      invisibleRoot.addChild(treeObjDbUser);
      invisibleRoot.addChild(treeObjDbPassword);
      invisibleRoot.addChild(treeObjDbJobId);
    }
  }

  class ViewLabelProvider extends LabelProvider implements ITableLabelProvider {

    public String getText(Object obj) {
      return obj.toString();
    }

    public Image getImage(Object obj) {
      String imageKey = ISharedImages.IMG_OBJ_ELEMENT;
      if (obj instanceof TreeParent)
        imageKey = ISharedImages.IMG_OBJ_FOLDER;
      return PlatformUI.getWorkbench().getSharedImages().getImage(imageKey);
    }

    public Image getColumnImage(Object obj, int columnIndex) {
      if (0 != columnIndex)
        return null;
      String imageKey = ISharedImages.IMG_OBJ_ELEMENT;
      if (obj instanceof TreeParent)
        imageKey = ISharedImages.IMG_OBJ_FOLDER;
      return PlatformUI.getWorkbench().getSharedImages().getImage(imageKey);
    }

    public String getColumnText(Object obj, int columnIndex) {
      TreeObject treeObject = (TreeObject) obj;
      if (0 != columnIndex)
        return treeObject.getValue();
      return treeObject.getName();
    }
  }

  class ConfigTreeCellModifier implements ICellModifier {

    public boolean canModify(Object element, String property) {
      if (!property.equals("Value"))
        return false;
      TreeObject o = (TreeObject) element;
      return o.canModify();
    }

    public Object getValue(Object element, String property) {
      TreeObject o = (TreeObject) element;
      return o;
    }

    public void modify(Object element, String property, Object value) {
      if (!property.equals("Value"))
        return;
      TreeItem treeItem = (TreeItem) element;
      TreeObject o = (TreeObject) treeItem.getData();
      o.setValue((String) value);
      viewer.refresh(true);
    }

  }

  class ConfigTreeCellEditor extends ComboBoxCellEditor {

    public ConfigTreeCellEditor(Composite parent) {
      super(parent, new String[] {});
    }

    /* (non-Javadoc)
     * @see org.eclipse.jface.viewers.ComboBoxCellEditor#doSetValue(java.lang.Object)
     */
    @Override
    protected void doSetValue(Object value) {
      TreeObject o = (TreeObject) value;
      this.setItems(o.getChoices());
      super.doSetValue(new Integer(0));
    }

    /* (non-Javadoc)
     * @see org.eclipse.jface.viewers.ComboBoxCellEditor#doGetValue()
     */
    @Override
    protected Object doGetValue() {
      Integer idx = (Integer) super.doGetValue();
      if (-1 == idx.intValue()) {
        CCombo combo = (CCombo)this.getControl();
        return combo.getText();
      }
      return getItems()[idx.intValue()];
    }

  }

  /**
   * The constructor.
   */
  public ConfigView() {
  }

  /**
   * This is a callback that will allow us to create the viewer and initialize
   * it.
   */
  public void createPartControl(Composite parent) {
		viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.FULL_SELECTION | SWT.V_SCROLL);
		viewer.setContentProvider(new ViewContentProvider());
		viewer.setLabelProvider(new ViewLabelProvider());
		// viewer.setSorter(new ViewerSorter());
		viewer.getTree().setLinesVisible(true);
		viewer.getTree().setHeaderVisible(true);
    TreeColumn treeColumn = new TreeColumn(viewer.getTree(), SWT.LEFT);
    treeColumn.setWidth(120);
    treeColumn.setText("Parameter");
    TreeColumn treeColumn1 = new TreeColumn(viewer.getTree(), SWT.LEFT);
    treeColumn1.setWidth(60);
    treeColumn1.setText("Value");
    viewer.setColumnProperties(new String[] {"Parameter", "Value"});
    viewer.setInput(getViewSite());
    getSite().setSelectionProvider(viewer);

    CellEditor[] editors = new CellEditor[2];
    editors[1] = new ConfigTreeCellEditor(viewer.getTree());
    viewer.setCellEditors(editors);
    viewer.setCellModifier(new ConfigTreeCellModifier());
	}

  /**
   * Passing the focus request to the viewer's control.
   */
  public void setFocus() {
    viewer.getControl().setFocus();
  }

  @Override
  public void init(IViewSite arg0) throws PartInitException {
    super.init(arg0);

    this.provider = Activator.getDefault().getDataProvider();
    this.provider.addPropertyChangeListener(this);
  }

  @Override
  public void dispose() {
    this.provider.removePropertyChangeListener(this);
    this.provider = null;
  }

  public void propertyChange(final PropertyChangeEvent event) {
    getSite().getShell().getDisplay().asyncExec(new Runnable(){
      public void run() {
        // Set simulator string
        if (event.getProperty().equals(Source.CONNECTED))
          treeObjSimulator.update();

        // Set time
        treeObjTime.update();

//        // Set config
//        if (event.getProperty().equals(Source.CONFIG_ADDED)) {
//          Source.Item item = (Source.Item) event.getNewValue();
//          AbstractParams config = null;
//          try {
//            config = (AbstractParams) provider.getDataSource().getContribution(item.getId());
//          } catch (CoreException e) {
//            Activator.log(e);
//            return;
//          }
//          try {
//            treeObjParameters.setConfig(config);
//          } catch (Exception e) {
//            e.printStackTrace();
//            treeObjParameters.clear();
//          }
//        }
//        else if (null == provider.getDataSource()
//            ||!provider.getDataSource().isConnected()) {
//          treeObjParameters.clear();
//        }

        // refresh
        viewer.refresh(true);
      }
    });
  }
}