package net.sf.jasperreports.engine.fill;

import java.util.Map;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExpression;

public class JRCalculator implements JRFillExpressionEvaluator {
  protected Map parsm = null;
  
  protected Map fldsm = null;
  
  protected Map varsm = null;
  
  protected JRFillVariable[] variables = null;
  
  protected JRFillGroup[] groups = null;
  
  protected JRFillElementDataset[] datasets = null;
  
  private JRFillVariable pageNumber = null;
  
  private JRFillVariable columnNumber = null;
  
  private final JREvaluator evaluator;
  
  protected JRCalculator(JREvaluator evaluator) {
    this.evaluator = evaluator;
  }
  
  protected void init(JRFillDataset dataset) throws JRException {
    this.parsm = dataset.parametersMap;
    this.fldsm = dataset.fieldsMap;
    this.varsm = dataset.variablesMap;
    this.variables = dataset.variables;
    this.groups = dataset.groups;
    this.datasets = dataset.elementDatasets;
    this.pageNumber = (JRFillVariable)this.varsm.get("PAGE_NUMBER");
    this.columnNumber = (JRFillVariable)this.varsm.get("COLUMN_NUMBER");
    byte whenResourceMissingType = dataset.getWhenResourceMissingType();
    this.evaluator.init(this.parsm, this.fldsm, this.varsm, whenResourceMissingType);
  }
  
  public JRFillVariable getPageNumber() {
    return this.pageNumber;
  }
  
  public JRFillVariable getColumnNumber() {
    return this.columnNumber;
  }
  
  public void calculateVariables() throws JRException {
    if (this.variables != null && this.variables.length > 0)
      for (int i = 0; i < this.variables.length; i++) {
        JRFillVariable variable = this.variables[i];
        Object expressionValue = evaluate(variable.getExpression());
        Object newValue = variable.getIncrementer().increment(variable, expressionValue, AbstractValueProvider.getCurrentValueProvider());
        variable.setValue(newValue);
        variable.setInitialized(false);
        if (variable.getIncrementType() == 5)
          variable.setIncrementedValue(variable.getValue()); 
      }  
    if (this.datasets != null && this.datasets.length > 0)
      for (int i = 0; i < this.datasets.length; i++) {
        JRFillElementDataset elementDataset = this.datasets[i];
        elementDataset.evaluate(this);
        if (elementDataset.getIncrementType() == 5)
          elementDataset.increment(); 
      }  
  }
  
  public void estimateVariables() throws JRException {
    if (this.variables != null && this.variables.length > 0) {
      JRFillVariable variable = null;
      Object expressionValue = null;
      Object newValue = null;
      for (int i = 0; i < this.variables.length; i++) {
        variable = this.variables[i];
        expressionValue = evaluateEstimated(variable.getExpression());
        newValue = variable.getIncrementer().increment(variable, expressionValue, AbstractValueProvider.getEstimatedValueProvider());
        variable.setEstimatedValue(newValue);
      } 
    } 
  }
  
  public void estimateGroupRuptures() throws JRException {
    JRFillGroup group = null;
    Object oldValue = null;
    Object estimatedValue = null;
    boolean groupHasChanged = false;
    boolean isTopLevelChange = false;
    if (this.groups != null && this.groups.length > 0)
      for (int i = 0; i < this.groups.length; i++) {
        group = this.groups[i];
        isTopLevelChange = false;
        if (!groupHasChanged) {
          oldValue = evaluateOld(group.getExpression());
          estimatedValue = evaluateEstimated(group.getExpression());
          if ((oldValue == null && estimatedValue != null) || (oldValue != null && !oldValue.equals(estimatedValue))) {
            groupHasChanged = true;
            isTopLevelChange = true;
          } 
        } 
        group.setHasChanged(groupHasChanged);
        group.setTopLevelChange(isTopLevelChange);
      }  
  }
  
  public void initializeVariables(byte resetType) throws JRException {
    if (this.variables != null && this.variables.length > 0)
      for (int i = 0; i < this.variables.length; i++) {
        incrementVariable(this.variables[i], resetType);
        initializeVariable(this.variables[i], resetType);
      }  
    if (this.datasets != null && this.datasets.length > 0)
      for (int i = 0; i < this.datasets.length; i++) {
        incrementDataset(this.datasets[i], resetType);
        initializeDataset(this.datasets[i], resetType);
      }  
  }
  
  private void incrementVariable(JRFillVariable variable, byte incrementType) {
    if (variable.getIncrementType() != 5) {
      boolean toIncrement = false;
      switch (incrementType) {
        case 1:
          toIncrement = true;
          break;
        case 2:
          toIncrement = (variable.getIncrementType() == 2 || variable.getIncrementType() == 3);
          break;
        case 3:
          toIncrement = (variable.getIncrementType() == 3);
          break;
        case 4:
          if (variable.getIncrementType() == 4) {
            JRFillGroup group = (JRFillGroup)variable.getIncrementGroup();
            toIncrement = group.hasChanged();
          } 
          break;
      } 
      if (toIncrement)
        variable.setIncrementedValue(variable.getValue()); 
    } else {
      variable.setIncrementedValue(variable.getValue());
    } 
  }
  
  private void incrementDataset(JRFillElementDataset elementDataset, byte incrementType) {
    if (elementDataset.getIncrementType() != 5) {
      boolean toIncrement = false;
      switch (incrementType) {
        case 1:
          toIncrement = true;
          break;
        case 2:
          toIncrement = (elementDataset.getIncrementType() == 2 || elementDataset.getIncrementType() == 3);
          break;
        case 3:
          toIncrement = (elementDataset.getIncrementType() == 3);
          break;
        case 4:
          if (elementDataset.getIncrementType() == 4) {
            JRFillGroup group = (JRFillGroup)elementDataset.getIncrementGroup();
            toIncrement = group.hasChanged();
          } 
          break;
      } 
      if (toIncrement)
        elementDataset.increment(); 
    } 
  }
  
  private void initializeVariable(JRFillVariable variable, byte resetType) throws JRException {
    if (variable.getResetType() != 5) {
      boolean toInitialize = false;
      switch (resetType) {
        case 1:
          toInitialize = true;
          break;
        case 2:
          toInitialize = (variable.getResetType() == 2 || variable.getResetType() == 3);
          break;
        case 3:
          toInitialize = (variable.getResetType() == 3);
          break;
        case 4:
          if (variable.getResetType() == 4) {
            JRFillGroup group = (JRFillGroup)variable.getResetGroup();
            toInitialize = group.hasChanged();
          } 
          break;
      } 
      if (toInitialize) {
        variable.setValue(evaluate(variable.getInitialValueExpression()));
        variable.setInitialized(true);
        variable.setIncrementedValue(null);
      } 
    } else {
      variable.setValue(evaluate(variable.getExpression()));
      variable.setIncrementedValue(variable.getValue());
    } 
  }
  
  private void initializeDataset(JRFillElementDataset elementDataset, byte resetType) {
    boolean toInitialize = false;
    switch (resetType) {
      case 1:
        toInitialize = true;
        break;
      case 2:
        toInitialize = (elementDataset.getResetType() == 2 || elementDataset.getResetType() == 3);
        break;
      case 3:
        toInitialize = (elementDataset.getResetType() == 3);
        break;
      case 4:
        if (elementDataset.getResetType() == 4) {
          JRFillGroup group = (JRFillGroup)elementDataset.getResetGroup();
          toInitialize = group.hasChanged();
        } 
        break;
    } 
    if (toInitialize)
      elementDataset.initialize(); 
  }
  
  public Object evaluate(JRExpression expression, byte evaluationType) throws JRException {
    Object value = null;
    switch (evaluationType) {
      case 1:
        value = evaluateOld(expression);
        return value;
      case 2:
        value = evaluateEstimated(expression);
        return value;
    } 
    value = evaluate(expression);
    return value;
  }
  
  public Object evaluateOld(JRExpression expression) throws JRExpressionEvalException {
    return this.evaluator.evaluateOld(expression);
  }
  
  public Object evaluateEstimated(JRExpression expression) throws JRExpressionEvalException {
    return this.evaluator.evaluateEstimated(expression);
  }
  
  public Object evaluate(JRExpression expression) throws JRExpressionEvalException {
    return this.evaluator.evaluate(expression);
  }
}
