package brn.sim.data;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import brn.sim.DriverRemote;
import brn.sim.DataManager.DataContribution;

public abstract class AbstractDiagramData implements Serializable, DataContribution {

  private static final long serialVersionUID = 1L;

  /**
   * Solid line style.
   */
  public static final int SOLID = 0;
  /**
   * Dashed line style.
   */
  public static final int DASHED = 1;
  /**
   * Heavy line style
   * @since 2.0
   */
  public static final int HEAVY = 2;
  /**
   * Highlighted line style.  Accomplished by drawing
   * the line over a contrasting polygon of the same shape.
   */
  public static final int HIGHLIGHT = 3;
  /**
   * Mark line style.
  **/
  public static final int MARK = 4;
  /**
   * Mark with connecting lines style.
   */
  public static final int MARK_LINE = 5;
  /**
   * Stroke.
   */
  public static final int STROKE = 6;

  protected Line line;

  private String subTitle;

  private int style;

  private String[] path;

  private String xLabel;
  private String xUnits;
  private String yLabel;
  private String yUnits;
  
  transient protected boolean active;
  
//  transient protected boolean shouldFireChanged = false;

//  transient private AbstractDiagramData[] listeners;

  /** ctor for hibernate */
  protected AbstractDiagramData() {}

  /**
   * Create a cuml data collector.
   *
   * @param xLabel   Label of the x axis.
   * @param xUnits   Unit of the x axis.
   * @param yLabel   Label of the y axis.
   * @param yUnits   Unit of the y axis.
   */
  public AbstractDiagramData(String[] path,
      String xLabel, String xUnits, String yLabel, String yUnits,
      int style) {
    this(new Line(path[path.length-1]), path, xLabel, xUnits,
        yLabel, yUnits, style);
  }

  /**
   * Create a cuml data collector.
   *
   * @param line     the line object to use.
   * @param xLabel   Label of the x axis.
   * @param xUnits   Unit of the x axis.
   * @param yLabel   Label of the y axis.
   * @param yUnits   Unit of the y axis.
   */
  public AbstractDiagramData(Line line, 
      String[] path, String xLabel, String xUnits,
      String yLabel, String yUnits, int style) {
    this.line = line;

    this.style = style;
    this.path = path;
    this.xLabel = xLabel;
    this.xUnits = xUnits;
    this.yLabel = yLabel;
    this.yUnits = yUnits;

    // TODO refactor to Line...
    if (this.line instanceof AveragedTimeLine) {
      AveragedTimeLine l = (AveragedTimeLine) this.line;
      String s = getPath(this.path) + " ";
      switch (l.getMode()) {
      case AveragedTimeLine.MODE_A:
        s += "(average over " + l.getInterval() + " " + xUnits + ")";
        break;
      case AveragedTimeLine.MODE_R:
        s += "(rate per " + l.getInterval() + " " + xUnits + ")";
        break;
      case AveragedTimeLine.MODE_WA2:
        s += "(weighted average over " + l.getInterval() + " " + xUnits + ")";
        break;
      }
      this.setSubTitle(s); 
    }
    else
      this.setSubTitle(getPath(this.path));
  }

  // //////////////////////////////////////////////////////////////////////
  // internals
  //

  public Line getLine() {
    return line;
  }

  public String[] getPath() {
    return path;
  }

  public static String getPath(String[] path) {
    String ret = "";
    for (int i = 0; i < path.length-1; i++)
      ret += path[i] + ", ";
    ret += path[path.length-1];
    return ret;
  }

  public int getType() {
    return DriverRemote.ITEM_TYPE_DATA;
  }

  /**
   * @return whether the data is empty
   * @see brn.sim.data.Line#empty()
   */
  public boolean empty() {
    return line.empty();
  }

  /**
   * @return the x data array
   * @see brn.sim.data.Line#getX()
   */
  public double[] getX() {
    return line.getX();
  }

  /**
   * @return the y data array
   * @see brn.sim.data.Line#getY()
   */
  public double[] getY() {
    return line.getY();
  }

  /**
   * @param title
   * @see brn.sim.data.Line#setTitle(java.lang.String)
   */
  public void setTitle(String title) {
    line.setTitle(title);
  }

  public int getStyle() {
    return style;
  }

  public String getXLabel() {
    return xLabel;
  }

  public String getXUnits() {
    return xUnits;
  }

  public String getYLabel() {
    return yLabel;
  }

  public String getYUnits() {
    return yUnits;
  }

  public String getTitle() {
    return line.getTitle();
  }

  public String getSubTitle() {
    return subTitle;
  }

  public void setSubTitle(String subTitle) {
    this.subTitle = subTitle;
  }

  // //////////////////////////////////////////////////////////////////////
  // internals
  //

  public String toString() {
    return line.getTitle();
  }

  // //////////////////////////////////////////////////////////////////////
  // internals
  //

//  protected void addListener(AbstractDiagramData listener) {
//    if (null == listeners)
//      listeners = new AbstractDiagramData[0];
//    AbstractDiagramData[] newListeners = new AbstractDiagramData[listeners.length+1];
//    System.arraycopy(listeners, 0, newListeners, 0, listeners.length);
//    listeners = newListeners;
//    listeners[listeners.length-1] = listener;
//    shouldFireChanged = true;
//  }
//
//  // final to allow inlining
//  protected final void fireChanged() {
//    for (int i = 0; null != listeners && i < listeners.length; i++)
//      listeners[i].changed(this);
//  }

  // final to allow inlining
  protected final int getLengthInternal() {
    return line.getLengthInternal();
  }

  // final to allow inlining
  protected final double[] getYInternal() {
    return line.getYInternal();
  }

  // final to allow inlining
  protected final double[] getXInternal() {
    return line.getXInternal();
  }

//  protected void changed(AbstractDiagramData source) {}

  /**
   * Intercept Java Serialization: trim the arrays before writing
   * the data to DB!
   */
  private void writeObject(ObjectOutputStream out) throws IOException {
    line.trim();
    out.defaultWriteObject();
  }

  public boolean isActive() {
    return active;
  }

  public void setActive(boolean active) {
    this.active = active;
  }
  
}
