package brn.gui.editors.timebar;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;

import brn.gui.datasource.Source;
import brn.gui.editors.SimulatorItemInput;
import brn.gui.editors.timebar.action.ZoomInAction;
import brn.gui.editors.timebar.action.ZoomOutAction;
import brn.sim.data.TimeBar;
import de.jaret.util.ui.timebars.AbstractTimeBarIntervalFilter;
import de.jaret.util.ui.timebars.TimeBarViewerDelegate;
import de.jaret.util.ui.timebars.model.DefaultRowHeader;
import de.jaret.util.ui.timebars.model.DefaultTimeBarModel;
import de.jaret.util.ui.timebars.model.DefaultTimeBarRowModel;
import de.jaret.util.ui.timebars.model.TimeBarModel;
import de.jaret.util.ui.timebars.swt.TimeBarViewer;
import de.jaret.util.ui.timebars.swt.renderer.DefaultTitleRenderer;
import de.jaret.util.ui.timebars.swt.util.actions.JaretTimeBarsActionFactory;

public class TimeBarEditor extends EditorPart implements IPropertyChangeListener {

	public static final String ID = "brn.gui.editors.TimeBarEditor";

  public class IntervalFilter extends AbstractTimeBarIntervalFilter {
    public long start = -1 /*us*/;
    public long end = 10000000 /*us*/;
    public int delta = 100000 /*events*/;
    public String detailFilter = null;

    public boolean isInResult(de.jaret.util.date.Interval interval) {
      Interval inter = (Interval) interval;
      long time = inter.getBegin().getDate().getTime();
      if (start <= time && time < end) {
        if (null == detailFilter
            || (null != inter.getDetail()
                && inter.getDetail().contains(detailFilter)))
        return true;
      }
      return false;
    }

    public void setStart(long start) {
      long oldStart = this.start;
      this.start = start;
      if (oldStart != start) {
        this.end = Long.MAX_VALUE;
        long startNs = this.start * 1000;
        List entries = timeBar.getEntries();
        int noEntries = 0;

        for (int i = 0; i < entries.size(); i++) {
          TimeBar.Entry entry = (TimeBar.Entry) entries.get(i);
          if (entry.start >= startNs)
            noEntries++;
          if (noEntries >= this.delta) {
            this.end = entry.end / 1000;
            break;
          }
        }
        int x = noEntries;
      }
      firePropertyChange("haha", null, null);
    }

    public void setDetailFilter(String detailFilter) {
      if (null != detailFilter && detailFilter.trim().length() == 0)
        detailFilter = null;
      this.detailFilter = detailFilter;
      firePropertyChange("haha", null, null);
    }
  };

  private TimeBar timeBar;

  private TimeBarViewer timeBarViewer;

  protected IntervalFilter timeBarIntervalFilter;


	public TimeBarEditor() {
		super();
    timeBarIntervalFilter = new IntervalFilter();
	}

  /*
   * (non-Javadoc)
   * @see org.eclipse.ui.texteditor.AbstractTextEditor#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
   */
  @Override
  public void init(IEditorSite site, IEditorInput input) throws PartInitException {
    setSite(site);
    setInput(input);

    SimulatorItemInput dataInput = (SimulatorItemInput) getEditorInput();
    if (null == dataInput
        || null == dataInput.getSimulator()
        || !dataInput.getSimulator().isConnected())
      throw new PartInitException("Simulator in error state.");

    dataInput.getSimulator().addPropertyChangeListener(this);

    try {
      timeBar = (TimeBar) dataInput.getSimulator().getContribution(dataInput.getId());
    } catch (CoreException e) {
      throw new PartInitException(e.getStatus());
    }
  }

  public TimeBarModel createModel() {
    DefaultTimeBarModel model = new DefaultTimeBarModel();
    List[] rowLists = new List[20];

    List entries = timeBar.getEntries();
    for (int i = 0; i < entries.size(); i++) {
      TimeBar.Entry entry = (TimeBar.Entry) entries.get(i);

      List<Interval> intervals = null;
      try {
        intervals = rowLists[entry.nodeId];
      }
      catch (IndexOutOfBoundsException e) {
        List[] tmp = rowLists;
        rowLists = new List[entry.nodeId + 20];
        System.arraycopy(tmp, 0, rowLists, 0, tmp.length);
      }

      if (null == intervals) {
        rowLists[entry.nodeId] = new ArrayList<Interval>();
        intervals = rowLists[entry.nodeId];
      }

      Interval interval = new Interval(entry.start, entry.end, entry.color,
          entry.text, entry.detail);
      intervals.add(interval);
    }
    for (int i = 0; i < rowLists.length; i++) {
      if (null == rowLists[i])
        continue;

      DefaultTimeBarRowModel row = new DefaultTimeBarRowModel(
          new DefaultRowHeader("Node " + i));
      model.addRow(row);
      row.addIntervals(rowLists[i]);
    }

    return model;
  }

  /*
   * (non-Javadoc)
   * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
   */
  @Override
  public void createPartControl(Composite parent) {
    String path = "";
    for (int i = 0; i < timeBar.getPath().length; i++)
      path += timeBar.getPath()[i] + ", ";

    setPartName(path);

    GridLayout gridLayout = new GridLayout();
    gridLayout.numColumns = 1;
    parent.setLayout(gridLayout);

    // create the model
    TimeBarModel model = createModel();

    // create timebarviewer
    timeBarViewer = new TimeBarViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL);
    GridData gd = new GridData(GridData.FILL_BOTH);
    timeBarViewer.setLayoutData(gd);

    // turn on milli mode
    timeBarViewer.setMilliAccuracy(true);
    timeBarViewer.setOverlapStrategy(
        new OverlapStrategy((TimeBarViewerDelegate) timeBarViewer.getData("delegate")));
    timeBarViewer.setTimeScalePosition(TimeBarViewer.TIMESCALE_POSITION_TOP);
    timeBarViewer.setModel(model);

    timeBarViewer.setPixelPerSecond(200);
    timeBarViewer.setDrawRowGrid(true);

    // use special grid and timescale renderers
    timeBarViewer.setGridRenderer(new MilliGrid());
    timeBarViewer.setTimeScaleRenderer(new MilliScale());
    timeBarViewer.registerTimeBarRenderer(Interval.class, new IntervalRenderer());

    // configure the title renderer with a background image and set the title
    DefaultTitleRenderer titleRenderer = new DefaultTitleRenderer();
    timeBarViewer.setTitleRenderer(titleRenderer);
    timeBarViewer.setTitle("Micros");

    // create a control panel for manipulation
    ControlPanel ctrl = new ControlPanel(parent, SWT.NULL, timeBarViewer);
    gd = new GridData(GridData.FILL_HORIZONTAL);
    ctrl.setLayoutData(gd);

    timeBarViewer.setIntervalFilter(timeBarIntervalFilter);
    timeBarIntervalFilter.setStart(0);
    getSite().setSelectionProvider(timeBarViewer);

    // TODO causes a runtime memory leak, finalize is not called!
//    MenuManager mm = new MenuManager();
//    mm.add(timeBarViewer.getActionFactory().createStdAction(JaretTimeBarsActionFactory.ACTION_CENTERSCALE));
//    mm.add(new ZoomOutAction(timeBarViewer, (TimeBarViewerDelegate)timeBarViewer.getData("delegate")));
//    mm.add(new ZoomInAction(timeBarViewer, (TimeBarViewerDelegate)timeBarViewer.getData("delegate")));
//    timeBarViewer.setScaleContextMenu(mm.createContextMenu(timeBarViewer));
//    timeBarViewer.setBodyContextMenu(mm.createContextMenu(timeBarViewer));
  }

  /**
   * Closes the editor programmatically.
   */
  public void close() {
    Display display = getSite().getShell().getDisplay();
    display.asyncExec(new Runnable() {
            public void run() {
                getSite().getPage().closeEditor(TimeBarEditor.this, false);
            }
        });
  }

  /*
   * (non-Javadoc)
   * @see org.eclipse.ui.part.WorkbenchPart#dispose()
   */
  @Override
  public void dispose() {
    SimulatorItemInput input = (SimulatorItemInput) getEditorInput();
    if (null != input.getSimulator())
      input.getSimulator().removePropertyChangeListener(this);

    super.dispose();
  }

  /*
   * (non-Javadoc)
   * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
   */
  @Override
  public void doSave(IProgressMonitor monitor) {
  }

  /*
   * (non-Javadoc)
   * @see org.eclipse.ui.part.EditorPart#doSaveAs()
   */
  @Override
  public void doSaveAs() {
  }

  /*
   * (non-Javadoc)
   * @see org.eclipse.ui.part.EditorPart#isDirty()
   */
  @Override
  public boolean isDirty() {
    return false;
  }

  /*
   * (non-Javadoc)
   * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
   */
  @Override
  public boolean isSaveAsAllowed() {
    return false;
  }

  /*
   * (non-Javadoc)
   * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
   */
  @Override
  public void setFocus() {
    // TODO Auto-generated method stub

  }

  /*
   * (non-Javadoc)
   * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
   */
  public void propertyChange(PropertyChangeEvent event) {
    if (event.getProperty().equals(Source.CONNECTED)
        ||event.getProperty().equals(Source.DATASOURCE_DISPOSED)) {
      SimulatorItemInput input = (SimulatorItemInput) getEditorInput();
      if (null == input)
        return;

      if (null == input.getSimulator()
          ||!input.getSimulator().isConnected()) {
        if (null != input.getSimulator())
          input.getSimulator().removePropertyChangeListener(this);
        close();
      }
    }
  }

  @Override
  protected void finalize() throws Throwable {
    // TODO Auto-generated method stub
    System.out.println("TimeBarEditor.finalize() called");
    super.finalize();
  }

}
