package net.sf.jasperreports.crosstabs.fill;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.fill.JRCalculable;

public class JRPercentageCalculatorFactory {
  private static final Map builtInCalculators = new HashMap();
  
  static {
    builtInCalculators.put(Float.class.getName(), new FloatPercentageCalculator());
    builtInCalculators.put(Double.class.getName(), new DoublePercentageCalculator());
    builtInCalculators.put(Integer.class.getName(), new IntegerPercentageCalculator());
    builtInCalculators.put(Long.class.getName(), new LongPercentageCalculator());
    builtInCalculators.put(Short.class.getName(), new ShortPercentageCalculator());
    builtInCalculators.put(Byte.class.getName(), new BytePercentageCalculator());
    builtInCalculators.put(BigDecimal.class.getName(), new BigDecimalPercentageCalculator());
    builtInCalculators.put(BigInteger.class.getName(), new BigIntegerPercentageCalculator());
  }
  
  private static final Map cachedCalculators = new HashMap();
  
  public static boolean hasBuiltInCalculator(Class valueClass) {
    return builtInCalculators.containsKey(valueClass.getName());
  }
  
  public static JRPercentageCalculator getPercentageCalculator(Class percentageCalculatorClass, Class valueClass) {
    JRPercentageCalculator calculator;
    if (percentageCalculatorClass == null) {
      calculator = (JRPercentageCalculator)builtInCalculators.get(valueClass.getName());
      if (calculator == null)
        throw new JRRuntimeException("Measure with type " + valueClass.getName() + " should specify a percentage calculator class."); 
    } else {
      calculator = (JRPercentageCalculator)cachedCalculators.get(percentageCalculatorClass.getName());
      if (calculator == null)
        try {
          calculator = percentageCalculatorClass.newInstance();
          cachedCalculators.put(percentageCalculatorClass.getName(), calculator);
        } catch (InstantiationException e) {
          throw new JRRuntimeException("Error while creating percentage calculator instance of " + percentageCalculatorClass + ".", e);
        } catch (IllegalAccessException e) {
          throw new JRRuntimeException("Error while creating percentage calculator instance of " + percentageCalculatorClass + ".", e);
        }  
    } 
    return calculator;
  }
  
  public static class BytePercentageCalculator implements JRPercentageCalculator {
    public Object calculatePercentage(JRCalculable value, JRCalculable total) {
      Byte totalVal = (Byte)total.getValue();
      Byte val = (Byte)value.getValue();
      byte percentage = 0;
      if (totalVal != null && totalVal.byteValue() != 0)
        percentage = (byte)(100 * val.byteValue() / totalVal.byteValue()); 
      return new Byte(percentage);
    }
  }
  
  public static class ShortPercentageCalculator implements JRPercentageCalculator {
    public Object calculatePercentage(JRCalculable value, JRCalculable total) {
      Short totalVal = (Short)total.getValue();
      Short val = (Short)value.getValue();
      short percentage = 0;
      if (totalVal != null && totalVal.shortValue() != 0)
        percentage = (short)(100 * val.shortValue() / totalVal.shortValue()); 
      return new Short(percentage);
    }
  }
  
  public static class IntegerPercentageCalculator implements JRPercentageCalculator {
    public Object calculatePercentage(JRCalculable value, JRCalculable total) {
      Integer totalVal = (Integer)total.getValue();
      Integer val = (Integer)value.getValue();
      int percentage = 0;
      if (totalVal != null && totalVal.intValue() != 0)
        percentage = 100 * val.intValue() / totalVal.intValue(); 
      return new Integer(percentage);
    }
  }
  
  public static class LongPercentageCalculator implements JRPercentageCalculator {
    public Object calculatePercentage(JRCalculable value, JRCalculable total) {
      Long totalVal = (Long)total.getValue();
      Long val = (Long)value.getValue();
      long percentage = 0L;
      if (totalVal != null && totalVal.longValue() != 0L)
        percentage = 100L * val.longValue() / totalVal.longValue(); 
      return new Long(percentage);
    }
  }
  
  public static class FloatPercentageCalculator implements JRPercentageCalculator {
    public Object calculatePercentage(JRCalculable value, JRCalculable total) {
      Float totalVal = (Float)total.getValue();
      Float val = (Float)value.getValue();
      float percentage = 0.0F;
      if (totalVal != null && totalVal.floatValue() != 0.0F)
        percentage = 100.0F * val.floatValue() / totalVal.floatValue(); 
      return new Float(percentage);
    }
  }
  
  public static class DoublePercentageCalculator implements JRPercentageCalculator {
    public Object calculatePercentage(JRCalculable value, JRCalculable total) {
      Double totalVal = (Double)total.getValue();
      Double val = (Double)value.getValue();
      double percentage = 0.0D;
      if (totalVal != null && totalVal.doubleValue() != 0.0D)
        percentage = 100.0D * val.doubleValue() / totalVal.doubleValue(); 
      return new Double(percentage);
    }
  }
  
  public static class BigDecimalPercentageCalculator implements JRPercentageCalculator {
    public Object calculatePercentage(JRCalculable value, JRCalculable total) {
      BigDecimal percentage, totalVal = (BigDecimal)total.getValue();
      BigDecimal val = (BigDecimal)value.getValue();
      if (totalVal != null && totalVal.doubleValue() != 0.0D) {
        percentage = val.multiply(BigDecimal.valueOf(100L)).divide(totalVal, 4);
      } else {
        percentage = BigDecimal.valueOf(0L);
      } 
      return percentage;
    }
  }
  
  public static class BigIntegerPercentageCalculator implements JRPercentageCalculator {
    public Object calculatePercentage(JRCalculable value, JRCalculable total) {
      BigInteger percentage, totalVal = (BigInteger)total.getValue();
      BigInteger val = (BigInteger)value.getValue();
      if (totalVal != null && totalVal.doubleValue() != 0.0D) {
        percentage = val.multiply(BigInteger.valueOf(100L)).divide(totalVal);
      } else {
        percentage = BigInteger.valueOf(0L);
      } 
      return percentage;
    }
  }
}
