package net.sf.jasperreports.engine.data;

import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.JRRewindableDataSource;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRSortField;

public class JRSortableDataSource implements JRRewindableDataSource {
  private List records = new ArrayList();
  
  private Iterator iterator = null;
  
  private Object[] currentRecord = null;
  
  private Map fieldIndexMap = new HashMap();
  
  protected Collator collator = null;
  
  public JRSortableDataSource(JRDataSource ds, JRField[] fields, JRSortField[] sortFields, Locale locale) throws JRException {
    if (fields == null)
      fields = new JRField[0]; 
    if (sortFields == null)
      sortFields = new JRSortField[0]; 
    verifySortFields(fields, sortFields);
    this.collator = Collator.getInstance(locale);
    for (int i = 0; i < fields.length; i++)
      this.fieldIndexMap.put(fields[i].getName(), new Integer(i)); 
    int[] sortIndexes = new int[sortFields.length];
    int[] sortOrders = new int[sortFields.length];
    boolean[] collatorFlags = new boolean[sortFields.length];
    for (int j = 0; j < sortFields.length; j++) {
      JRSortField sortField = sortFields[j];
      sortIndexes[j] = ((Integer)this.fieldIndexMap.get(sortField.getName())).intValue();
      sortOrders[j] = (0 == sortField.getOrder()) ? 1 : -1;
      collatorFlags[j] = false;
      for (int k = 0; k < fields.length; k++) {
        JRField field = fields[k];
        if (sortField.getName().equals(field.getName())) {
          collatorFlags[j] = String.class.getName().equals(field.getValueClassName());
          break;
        } 
      } 
    } 
    if (ds != null)
      while (ds.next()) {
        Object[] record = new Object[fields.length];
        for (int k = 0; k < fields.length; k++)
          record[k] = ds.getFieldValue(fields[k]); 
        this.records.add(record);
      }  
    Collections.sort(this.records, new DataSourceComparator(sortIndexes, sortOrders, collatorFlags));
    this.iterator = this.records.iterator();
  }
  
  public boolean next() {
    boolean hasNext = false;
    if (this.iterator != null) {
      hasNext = this.iterator.hasNext();
      if (hasNext)
        this.currentRecord = this.iterator.next(); 
    } 
    return hasNext;
  }
  
  public Object getFieldValue(JRField jrField) {
    Integer fieldIndex = (Integer)this.fieldIndexMap.get(jrField.getName());
    if (fieldIndex == null)
      throw new JRRuntimeException("Field \"" + jrField.getName() + "\" not found in sortable data source."); 
    return this.currentRecord[fieldIndex.intValue()];
  }
  
  public void moveFirst() {
    this.iterator = this.records.iterator();
  }
  
  public static void verifySortFields(JRField[] fields, JRSortField[] sortFields) {
    if (fields != null && sortFields != null)
      for (int i = 0; i < sortFields.length; i++) {
        String sortFieldName = sortFields[i].getName();
        boolean isFound = false;
        int j = 0;
        while (!isFound && j < fields.length) {
          isFound = sortFieldName.equals(fields[j].getName());
          j++;
        } 
        if (!isFound)
          throw new JRRuntimeException("Sort field \"" + sortFieldName + "\" not found in the list of data source fields."); 
      }  
  }
  
  class DataSourceComparator implements Comparator {
    int[] sortIndexes = null;
    
    int[] sortOrders = null;
    
    boolean[] collatorFlags = null;
    
    private final JRSortableDataSource this$0;
    
    public DataSourceComparator(int[] sortIndexes, int[] sortOrders, boolean[] collatorFlags) {
      this.sortIndexes = sortIndexes;
      this.sortOrders = sortOrders;
      this.collatorFlags = collatorFlags;
    }
    
    public int compare(Object arg1, Object arg2) {
      Object[] record1 = (Object[])arg1;
      Object[] record2 = (Object[])arg2;
      int ret = 0;
      for (int i = 0; i < this.sortIndexes.length; i++) {
        int sortIndex = this.sortIndexes[i];
        Comparable field1 = (Comparable)record1[sortIndex];
        Comparable field2 = (Comparable)record2[sortIndex];
        if (field1 == null) {
          ret = (field2 == null) ? 0 : -1;
        } else if (field2 == null) {
          ret = 1;
        } else if (this.collatorFlags[i]) {
          ret = JRSortableDataSource.this.collator.compare(field1, field2);
        } else {
          ret = field1.compareTo(field2);
        } 
        ret *= this.sortOrders[i];
        if (ret != 0)
          return ret; 
      } 
      return ret;
    }
  }
}
