/*
 * Copyright
 *   2007 axYus - www.axyus.com
 *   2005 C.Marchand - christophe.marchand@axyus.com
 *
 * This file is part of XEMELIOS.
 *
 * XEMELIOS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * XEMELIOS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with XEMELIOS; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package fr.gouv.finances.cp.xemelios.ui.resulttable;

import fr.gouv.finances.cp.utils.Pair;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.TreeSet;
import java.util.Vector;

import javax.swing.table.AbstractTableModel;

import org.apache.log4j.Logger;

import fr.gouv.finances.cp.utils.Amount;
import fr.gouv.finances.cp.xemelios.common.config.ChampModel;
import fr.gouv.finances.cp.xemelios.common.config.HiddenModel;
import fr.gouv.finances.cp.xemelios.common.config.ListeResultatModel;
import fr.gouv.finances.cp.xemelios.data.DataResultSet;

/**
 * Represente la tble de resultats de recherche
 * @author chm
 */
public class EtatResultTableModel extends AbstractTableModel {
	private static Logger logger = Logger.getLogger(EtatResultTableModel.class);
	/**
	 * Comment for <code>serialVersionUID</code>
	 */
	private static final long serialVersionUID = 3546078073121223990L;
	private ListeResultatModel lrm;
	private ArrayList<DataHandler> data;
	private DataResultSet rs;
	private int orderByCol, sortOrder;
	private Class[] datatypes=null;
	
	public EtatResultTableModel(ListeResultatModel lrm, TreeSet<DataHandler> input, DataResultSet rs) {
		super();
		this.lrm = lrm;
		this.data = new ArrayList<DataHandler>();
		this.rs = rs;
		for(DataHandler dh:input) {
		    for(int i=0;i<lrm.getListeChamps().size();i++) {
		        try { dh.getValueAtColumn(i); } catch(Throwable t) {}
		    }
	        data.add(dh);
		}
		if(data.size()>0) {
		    DataHandler dh = data.get(0);
		    this.orderByCol = dh.getOrderByColumn();
		    this.sortOrder = dh.getSortOrder();
		} else {
		    this.orderByCol = 0;
		    this.sortOrder = DataHandler.ORDER_ASC;
		}
		// on precalcule tout
	}
	public EtatResultTableModel(ListeResultatModel lrm, Vector<DataHandler> input) {
		super();
		this.lrm = lrm;
		this.data = new ArrayList<DataHandler>();
		for(DataHandler dh:input) {
		    for(int i=0;i<lrm.getListeChamps().size();i++) {
		        try { dh.getValueAtColumn(i); } catch(Throwable t) {}
		    }
	        data.add(dh);
		}
		if(data.size()>0) {
		    DataHandler dh = data.get(0);
		    this.orderByCol = dh.getOrderByColumn();
		    this.sortOrder = dh.getSortOrder();
		} else {
		    this.orderByCol = 0;
		    this.sortOrder = DataHandler.ORDER_ASC;
		}
		// on precalcule tout
	}

	public int getRowCount() {
		if(data==null) return 0;
		int ret = data.size();
		return ret;
	}
	public int getColumnCount() {
		return lrm.getListeChamps().size();
	}
	@Override
	public String getColumnName(int col) {
		ChampModel cm = lrm.getListeChamps().elementAt(col);
		return cm.getLibelle();
	}
	@Override
	public Class< ? > getColumnClass(int col) {
	    if(datatypes==null) datatypes = new Class[getColumnCount()];
	    if(datatypes[col]==null) {
	        ChampModel cm = lrm.getListeChamps().elementAt(col);
	        datatypes[col]=cm.getDataClass();
	    }
		return datatypes[col]; 
	}
	@Override
	public boolean isCellEditable(int row, int column) {
		return getColumnClass(column).equals(PjRefHandler.class);
	}
	public Object getValueAt(int row, int col) {
		try {
		    Object o = getRowAt(row,col).getValueAtColumn(col);
		    return o;
		} catch(ParseException t) {
			ChampModel cm = lrm.getListeChamps().elementAt(col);
		    logger.warn("Type non attendu: "+data+" n'est pas du type "+cm.getDataClass().getName());
		    return null;
		}
	}
	public Object getInnerValueAt(int row, int col) {
	    Object ret = getRowAt(row,col).getInnerDataAt(col);
	    return (ret==null ? "" : ret);
	}
	public DataHandler getRowAt(int row, int col) {
		DataHandler dh = data.get(row);
		return dh;
	}
	@Override
	public void setValueAt(Object arg0, int arg1, int arg2) {}
//	@Override
//	public void addTableModelListener(TableModelListener arg0) {
//	}
//	@Override
//	public void removeTableModelListener(TableModelListener arg0) {
//	}
	public void setData(TreeSet<DataHandler> input) {
		this.data = new ArrayList<DataHandler>();
		for(DataHandler dh:input) {
		    for(int i=0;i<lrm.getListeChamps().size();i++) {
		        try { dh.getValueAtColumn(i); } catch(Throwable t) {}
		    }
                    data.add(dh);
		}
		if(data.size()>0) {
		    DataHandler dh = data.get(0);
		    this.orderByCol = dh.getOrderByColumn();
		    this.sortOrder = dh.getSortOrder();
		} else {
		    this.orderByCol = 0;
		    this.sortOrder = DataHandler.ORDER_ASC;
		}
	}
	public int sortRequired(int col) {
		logger.debug("sort required: "+col);
		if(data==null || data.size()==0) return 0;
		int orderByCol = 0;
		int sortOrder = 0;
		DataHandler dh = data.get(0);
		if(dh.getOrderByColumn()==col) {
			if(dh.getSortOrder()==DataHandler.ORDER_ASC) sortOrder = DataHandler.ORDER_DESC;
			else sortOrder = DataHandler.ORDER_ASC;
			orderByCol=col;
		} else {
			orderByCol = col;
			sortOrder = DataHandler.ORDER_ASC;
		}
		this.orderByCol = orderByCol;
		this.sortOrder = sortOrder;
		TreeSet<DataHandler> ts = new TreeSet<DataHandler>();
		for(DataHandler dh2:data) {
			dh2.setOrderByColumn(orderByCol,sortOrder);
			ts.add(dh2);
		}
		setData(ts);
		fireTableStructureChanged();
		return sortOrder;
	}
	/**
	 * @return Returns the orderByCol.
	 */
	public int getOrderByCol() {
		return orderByCol;
	}
	/**
	 * @return Returns the sortOrder.
	 */
	public int getSortOrder() {
		return sortOrder;
	}
	public String getCsvBuffer(String fieldSeparator,boolean exportAll,int[] selectedRows) {
	    String lineSeparator = System.getProperty("line.separator");
	    DecimalFormat df = new DecimalFormat("#0.00");
	    SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
	    StringBuffer buff = new StringBuffer();
	    for(ChampModel cm:lrm.getListeChamps()) {
	        buff.append(cm.getLibelle()).append(fieldSeparator);
	    }
	    buff.append(lineSeparator);
	    for(int row=0;row<getRowCount();row++) {
	        if(exportAll || Arrays.binarySearch(selectedRows,row)>=0) {
		        for(int col=0;col<getColumnCount();col++) {
		            Object value = getValueAt(row,col);
		            String strVal = null;
		            if(value instanceof Float) {
		                //double d = ((Float)value).doubleValue();
		                strVal = df.format((Float)value);
		                strVal = strVal.replaceAll("\\.",",");
		                logger.debug("double: "+strVal);
		            } else if(value instanceof Amount) {
		                //double d = ((Amount)value).doubleValue();
		                strVal = df.format((Amount)value);
		                strVal = strVal.replaceAll("\\.",",");
		                logger.debug("amount: "+strVal);
		            } else if(value instanceof Date) {
		            	strVal = sdf.format((Date)value);
		            } else {
		                strVal = value==null ? "" : value.toString();
		            }
		            buff.append(strVal).append(fieldSeparator);
		        }
		        buff.append(lineSeparator);
	        }
	    }
	    return buff.toString();
	}
	public String getCsvBuffer(String fieldSeparator,String lineHeader) {
	    String lineSeparator = System.getProperty("line.separator");
	    DecimalFormat df = new DecimalFormat("#0.00");
	    SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
	    StringBuffer buff = new StringBuffer();
	    for(int row=0;row<getRowCount();row++) {
	        buff.append(lineHeader);
	        for(int col=0;col<getColumnCount();col++) {
	            Object value = getValueAt(row,col);
	            String strVal = null;
	            if(value instanceof Float) {
	                //double d = ((Float)value).doubleValue();
	                strVal = df.format((Float)value);
	                strVal = strVal.replaceAll("\\.",",");
	                logger.debug("double: "+strVal);
	            } else if(value instanceof Amount) {
	                //double d = ((Amount)value).doubleValue();
	                strVal = df.format((Amount)value);
	                strVal = strVal.replaceAll("\\.",",");
	                logger.debug("amount: "+strVal);
	            } else if(value instanceof Date) {
	            	strVal = sdf.format((Date)value);
	            } else {
	                strVal = value==null ? "" : value.toString();
	            }
	            buff.append(strVal).append(fieldSeparator);
	        }
	        buff.append(lineSeparator);
	    }
	    return buff.toString();
	}
	
	public void dispose() {
	    lrm = null;
	    data.clear();
	    data = null;
	    if(datatypes!=null)
	        for(int i=0;i<datatypes.length;i++) datatypes[i]=null;
	}
	public DataResultSet getResultSet() { return rs; }
	
	/**
	 * Indicates whether a column allows the sum operations.
	 * A column permits these operations only if the data is of type {integer, date, decimal}
	 * and the optional attribute "somme-possible" is set to <b>true</b>.
	 * @param columnIndex the index (in the model) of the column
	 * @return <b>true</b> if this column allows sum operations, <b>false</b> otherwise
	 */
	public boolean isSumOperationPermittedOnColumn(int columnIndex) {
		boolean result = false;
		Vector<ChampModel> listeChamps = lrm.getListeChamps();
		if(columnIndex<=listeChamps.size()) {
			ChampModel cm = listeChamps.get(columnIndex);
			if((cm.getDatatype().equals(ChampModel.DATATYPE_INTEGER) || cm.getDatatype().equals(ChampModel.DATATYPE_DECIMAL) || cm.getDatatype().equals(ChampModel.DATATYPE_AMOUNT)
					|| cm.getDatatype().equals(ChampModel.DATATYPE_DATE)) && cm.isSommePossible()) {
				result = true;
			}
		}		
		return result;
	}
	
	/**
	 * Returns the data type of a column, given the column index
	 * @param columnIndex The index of the column we want the data type of
	 * @return A data type, or an empty String if an error occured
	 */
	public String getColumnDatatype(int columnIndex) {
		String result = "";
		Vector<ChampModel> listeChamps = lrm.getListeChamps();
		if(columnIndex<=listeChamps.size()) {
			ChampModel cm = listeChamps.get(columnIndex);
			result = cm.getDatatype();
		}
		
		return result;
	}
	
	/**
	 * Returns the absolute XPath for the given column index 
	 * @param columnIndex The index of the column we want the XPath of
	 * @return an absolute XPath String, or an empty String if an error occured
	 */
	public String getColumnXPath(int columnIndex) {
		String result = "";
		Vector<ChampModel> listeChamps = lrm.getListeChamps();
		if(columnIndex<=listeChamps.size()) {
			ChampModel cm = listeChamps.get(columnIndex);
			result = lrm.getParent().getPath().getFormattedPath()+"/"+cm.getPath().getFormattedPath();
		}
//logger.debug("getColumnXPath = "+result);
		return result;
	}

	/**
	 * Returns the label for the header of a column, given its index
	 * @param columnIndex The index of the column
	 * @return the header of the column, an empty String if an error occured
	 */
	public String getColumnHeader(int columnIndex) {
		String result = "";
		Vector<ChampModel> listeChamps = lrm.getListeChamps();
		if(columnIndex<=listeChamps.size()) {
			ChampModel cm = listeChamps.get(columnIndex);
			result = cm.getLibelle();
		}
		return result;
	}
	
	public Vector<ChampModel> getListeChamps() { return lrm.getListeChamps(); }
        public Vector<HiddenModel> getHiddens() { return lrm.getHiddens(); }
        public ListeResultatModel getListeResultat() { return lrm; }
        public Pair getCollectivite() { return rs.getCollectivite(); }
        public Pair getBudget() { return rs.getBudget(); }
}
