package net.sf.jasperreports.crosstabs.fill.calculation;

import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.fill.AbstractValueProvider;
import net.sf.jasperreports.engine.fill.JRCalculable;
import net.sf.jasperreports.engine.fill.JRDistinctCountExtendedIncrementerFactory;
import net.sf.jasperreports.engine.fill.JRExtendedIncrementer;
import net.sf.jasperreports.engine.fill.JRExtendedIncrementerFactory;

public class MeasureDefinition {
  protected final byte calculation;
  
  protected final JRExtendedIncrementerFactory incrementerFactory;
  
  protected final Class valueClass;
  
  protected final boolean isSystemDefined;
  
  public MeasureDefinition(Class valueClass, byte calculation, JRExtendedIncrementerFactory incrementerFactory) {
    this(valueClass, calculation, incrementerFactory, false);
  }
  
  protected MeasureDefinition(Class valueClass, byte calculation, JRExtendedIncrementerFactory incrementerFactory, boolean isSystemDefined) {
    this.valueClass = valueClass;
    this.calculation = calculation;
    this.incrementerFactory = incrementerFactory;
    this.isSystemDefined = isSystemDefined;
  }
  
  public static MeasureDefinition createHelperMeasure(MeasureDefinition measure, byte helperCalculation) {
    return new MeasureDefinition(measure.valueClass, helperCalculation, measure.incrementerFactory, true);
  }
  
  public static MeasureDefinition createDistinctCountHelperMeasure(MeasureDefinition measure) {
    return new MeasureDefinition(measure.valueClass, (byte)0, (JRExtendedIncrementerFactory)JRDistinctCountExtendedIncrementerFactory.getInstance(), true);
  }
  
  public byte getCalculation() {
    return this.calculation;
  }
  
  public JRExtendedIncrementerFactory getIncrementerFactory() {
    return this.incrementerFactory;
  }
  
  public JRExtendedIncrementer getIncrementer() {
    return this.incrementerFactory.getExtendedIncrementer(this.calculation);
  }
  
  protected boolean isSystemDefined() {
    return this.isSystemDefined;
  }
  
  public Class getValueClass() {
    return this.valueClass;
  }
  
  protected static final AbstractValueProvider VALUE_PROVIDER = new AbstractValueProvider() {
      public Object getValue(JRCalculable calculable) {
        return calculable.getValue();
      }
    };
  
  public class MeasureValue implements JRCalculable {
    private Object value;
    
    private MeasureValue[] helpers;
    
    private boolean initialized;
    
    private JRExtendedIncrementer incrementer;
    
    private final MeasureDefinition this$0;
    
    public MeasureValue() {
      this.value = null;
      this.helpers = new MeasureValue[3];
      this.incrementer = MeasureDefinition.this.getIncrementer();
      init();
    }
    
    protected void init() {
      this.value = this.incrementer.initialValue();
      setInitialized(true);
    }
    
    public void addValue(Object addValue) throws JRException {
      this.value = this.incrementer.increment(this, addValue, MeasureDefinition.VALUE_PROVIDER);
      setInitialized(false);
    }
    
    public void addValue(MeasureValue measureValue) throws JRException {
      if (!measureValue.isInitialized()) {
        this.value = this.incrementer.combine(this, measureValue, MeasureDefinition.VALUE_PROVIDER);
        setInitialized(false);
      } 
    }
    
    public Object getValue() {
      return this.value;
    }
    
    public String toString() {
      return String.valueOf(getValue());
    }
    
    public MeasureValue setHelper(MeasureValue helperVariable, byte type) {
      MeasureValue old = this.helpers[type];
      this.helpers[type] = helperVariable;
      return old;
    }
    
    public boolean isInitialized() {
      return this.initialized;
    }
    
    public Object getIncrementedValue() {
      return this.value;
    }
    
    public JRCalculable getHelperVariable(byte helperType) {
      return this.helpers[helperType];
    }
    
    public void setInitialized(boolean isInitialized) {
      this.initialized = isInitialized;
    }
  }
}
