package net.sf.jasperreports.engine.base;

import java.awt.Color;
import java.io.Serializable;
import net.sf.jasperreports.engine.JRCommonElement;
import net.sf.jasperreports.engine.JRDefaultStyleProvider;
import net.sf.jasperreports.engine.JRElement;
import net.sf.jasperreports.engine.JRElementGroup;
import net.sf.jasperreports.engine.JRExpression;
import net.sf.jasperreports.engine.JRGroup;
import net.sf.jasperreports.engine.JRPropertiesHolder;
import net.sf.jasperreports.engine.JRPropertiesMap;
import net.sf.jasperreports.engine.JRPropertyExpression;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRStyle;
import net.sf.jasperreports.engine.JRVisitable;
import net.sf.jasperreports.engine.design.events.JRChangeEventsSupport;
import net.sf.jasperreports.engine.design.events.JRPropertyChangeSupport;
import net.sf.jasperreports.engine.util.JRStyleResolver;

public abstract class JRBaseElement implements JRElement, Serializable, JRChangeEventsSupport {
  private static final long serialVersionUID = 10200L;
  
  public static final String PROPERTY_POSITION_TYPE = "positionType";
  
  public static final String PROPERTY_PRINT_IN_FIRST_WHOLE_BAND = "isPrintInFirstWholeBand";
  
  public static final String PROPERTY_PRINT_REPEATED_VALUES = "isPrintRepeatedValues";
  
  public static final String PROPERTY_PRINT_WHEN_DETAIL_OVERFLOWS = "isPrintWhenDetailOverflows";
  
  public static final String PROPERTY_REMOVE_LINE_WHEN_BLANK = "isRemoveLineWhenBlank";
  
  public static final String PROPERTY_STRETCH_TYPE = "stretchType";
  
  public static final String PROPERTY_WIDTH = "width";
  
  public static final String PROPERTY_X = "x";
  
  protected String key = null;
  
  protected byte positionType;
  
  protected byte stretchType;
  
  protected boolean isPrintRepeatedValues = true;
  
  protected Byte mode;
  
  protected int x = 0;
  
  protected int y = 0;
  
  protected int width = 0;
  
  protected int height = 0;
  
  protected boolean isRemoveLineWhenBlank = false;
  
  protected boolean isPrintInFirstWholeBand = false;
  
  protected boolean isPrintWhenDetailOverflows = false;
  
  protected Color forecolor = null;
  
  protected Color backcolor = null;
  
  protected JRExpression printWhenExpression = null;
  
  protected JRGroup printWhenGroupChanges = null;
  
  protected JRElementGroup elementGroup = null;
  
  protected JRDefaultStyleProvider defaultStyleProvider;
  
  protected JRStyle parentStyle;
  
  protected String parentStyleNameReference;
  
  private JRPropertiesMap propertiesMap;
  
  private JRPropertyExpression[] propertyExpressions;
  
  private transient JRPropertyChangeSupport eventSupport;
  
  protected JRBaseElement(JRDefaultStyleProvider defaultStyleProvider) {
    this.defaultStyleProvider = defaultStyleProvider;
  }
  
  protected JRBaseElement(JRElement element, JRBaseObjectFactory factory) {
    factory.put(element, this);
    this.defaultStyleProvider = factory.getDefaultStyleProvider();
    this.parentStyle = factory.getStyle(element.getStyle());
    this.parentStyleNameReference = element.getStyleNameReference();
    this.key = element.getKey();
    this.positionType = element.getPositionType();
    this.stretchType = element.getStretchType();
    this.isPrintRepeatedValues = element.isPrintRepeatedValues();
    this.mode = element.getOwnMode();
    this.x = element.getX();
    this.y = element.getY();
    this.width = element.getWidth();
    this.height = element.getHeight();
    this.isRemoveLineWhenBlank = element.isRemoveLineWhenBlank();
    this.isPrintInFirstWholeBand = element.isPrintInFirstWholeBand();
    this.isPrintWhenDetailOverflows = element.isPrintWhenDetailOverflows();
    this.forecolor = element.getOwnForecolor();
    this.backcolor = element.getOwnBackcolor();
    this.printWhenExpression = factory.getExpression(element.getPrintWhenExpression());
    this.printWhenGroupChanges = factory.getGroup(element.getPrintWhenGroupChanges());
    this.elementGroup = (JRElementGroup)factory.getVisitResult((JRVisitable)element.getElementGroup());
    this.propertiesMap = JRPropertiesMap.getPropertiesClone((JRPropertiesHolder)element);
    copyPropertyExpressions(element, factory);
  }
  
  private void copyPropertyExpressions(JRElement element, JRBaseObjectFactory factory) {
    JRPropertyExpression[] props = element.getPropertyExpressions();
    if (props != null && props.length > 0) {
      this.propertyExpressions = new JRPropertyExpression[props.length];
      for (int i = 0; i < props.length; i++)
        this.propertyExpressions[i] = factory.getPropertyExpression(props[i]); 
    } 
  }
  
  public JRDefaultStyleProvider getDefaultStyleProvider() {
    return this.defaultStyleProvider;
  }
  
  protected JRStyle getBaseStyle() {
    if (this.parentStyle != null)
      return this.parentStyle; 
    if (this.defaultStyleProvider != null)
      return this.defaultStyleProvider.getDefaultStyle(); 
    return null;
  }
  
  public String getKey() {
    return this.key;
  }
  
  public byte getPositionType() {
    return this.positionType;
  }
  
  public void setPositionType(byte positionType) {
    byte old = this.positionType;
    this.positionType = positionType;
    getEventSupport().firePropertyChange("positionType", old, this.positionType);
  }
  
  public byte getStretchType() {
    return this.stretchType;
  }
  
  public void setStretchType(byte stretchType) {
    byte old = this.stretchType;
    this.stretchType = stretchType;
    getEventSupport().firePropertyChange("stretchType", old, this.stretchType);
  }
  
  public boolean isPrintRepeatedValues() {
    return this.isPrintRepeatedValues;
  }
  
  public void setPrintRepeatedValues(boolean isPrintRepeatedValues) {
    boolean old = this.isPrintRepeatedValues;
    this.isPrintRepeatedValues = isPrintRepeatedValues;
    getEventSupport().firePropertyChange("isPrintRepeatedValues", old, this.isPrintRepeatedValues);
  }
  
  public byte getMode() {
    return JRStyleResolver.getMode((JRCommonElement)this, (byte)1);
  }
  
  public Byte getOwnMode() {
    return this.mode;
  }
  
  public void setMode(byte mode) {
    setMode(new Byte(mode));
  }
  
  public void setMode(Byte mode) {
    Object old = this.mode;
    this.mode = mode;
    getEventSupport().firePropertyChange("mode", old, this.mode);
  }
  
  public int getX() {
    return this.x;
  }
  
  public void setX(int x) {
    int old = this.x;
    this.x = x;
    getEventSupport().firePropertyChange("x", old, this.x);
  }
  
  public int getY() {
    return this.y;
  }
  
  public int getWidth() {
    return this.width;
  }
  
  public void setWidth(int width) {
    int old = this.width;
    this.width = width;
    getEventSupport().firePropertyChange("width", old, this.width);
  }
  
  public int getHeight() {
    return this.height;
  }
  
  public boolean isRemoveLineWhenBlank() {
    return this.isRemoveLineWhenBlank;
  }
  
  public void setRemoveLineWhenBlank(boolean isRemoveLine) {
    boolean old = this.isRemoveLineWhenBlank;
    this.isRemoveLineWhenBlank = isRemoveLine;
    getEventSupport().firePropertyChange("isRemoveLineWhenBlank", old, this.isRemoveLineWhenBlank);
  }
  
  public boolean isPrintInFirstWholeBand() {
    return this.isPrintInFirstWholeBand;
  }
  
  public void setPrintInFirstWholeBand(boolean isPrint) {
    boolean old = this.isPrintInFirstWholeBand;
    this.isPrintInFirstWholeBand = isPrint;
    getEventSupport().firePropertyChange("isPrintInFirstWholeBand", old, this.isPrintInFirstWholeBand);
  }
  
  public boolean isPrintWhenDetailOverflows() {
    return this.isPrintWhenDetailOverflows;
  }
  
  public void setPrintWhenDetailOverflows(boolean isPrint) {
    boolean old = this.isPrintWhenDetailOverflows;
    this.isPrintWhenDetailOverflows = isPrint;
    getEventSupport().firePropertyChange("isPrintWhenDetailOverflows", old, this.isPrintWhenDetailOverflows);
  }
  
  public Color getForecolor() {
    return JRStyleResolver.getForecolor((JRCommonElement)this);
  }
  
  public Color getOwnForecolor() {
    return this.forecolor;
  }
  
  public void setForecolor(Color forecolor) {
    Object old = this.forecolor;
    this.forecolor = forecolor;
    getEventSupport().firePropertyChange("forecolor", old, this.forecolor);
  }
  
  public Color getBackcolor() {
    return JRStyleResolver.getBackcolor((JRCommonElement)this);
  }
  
  public Color getOwnBackcolor() {
    return this.backcolor;
  }
  
  public void setBackcolor(Color backcolor) {
    Object old = this.backcolor;
    this.backcolor = backcolor;
    getEventSupport().firePropertyChange("backcolor", old, this.backcolor);
  }
  
  public JRExpression getPrintWhenExpression() {
    return this.printWhenExpression;
  }
  
  public JRGroup getPrintWhenGroupChanges() {
    return this.printWhenGroupChanges;
  }
  
  public JRElementGroup getElementGroup() {
    return this.elementGroup;
  }
  
  public JRStyle getStyle() {
    return this.parentStyle;
  }
  
  public String getStyleNameReference() {
    return this.parentStyleNameReference;
  }
  
  public JRPropertyChangeSupport getEventSupport() {
    synchronized (this) {
      if (this.eventSupport == null)
        this.eventSupport = new JRPropertyChangeSupport(this); 
    } 
    return this.eventSupport;
  }
  
  public Object clone() {
    JRBaseElement clone = null;
    try {
      clone = (JRBaseElement)super.clone();
    } catch (CloneNotSupportedException e) {
      throw new JRRuntimeException(e);
    } 
    if (this.printWhenExpression != null)
      clone.printWhenExpression = (JRExpression)this.printWhenExpression.clone(); 
    return clone;
  }
  
  public Object clone(JRElementGroup parentGroup) {
    JRBaseElement clone = (JRBaseElement)clone();
    clone.elementGroup = parentGroup;
    return clone;
  }
  
  public boolean hasProperties() {
    return (this.propertiesMap != null && this.propertiesMap.hasProperties());
  }
  
  public JRPropertiesMap getPropertiesMap() {
    if (this.propertiesMap == null)
      this.propertiesMap = new JRPropertiesMap(); 
    return this.propertiesMap;
  }
  
  public JRPropertiesHolder getParentProperties() {
    return null;
  }
  
  public JRPropertyExpression[] getPropertyExpressions() {
    return this.propertyExpressions;
  }
}
