package lu.tudor.santec.gecamed.core.gui.utils;

import java.awt.Dimension;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;

/**
 * @author jens.ferring(at)tudor.lu
 * 
 * @version
 * <br>$Log: AutoResizeTable.java,v $
 * <br>Revision 1.2  2014-01-02 15:14:49  ferring
 * <br>header has 4px space on each side
 * <br>
 * <br>Revision 1.1  2013-11-21 09:44:52  ferring
 * <br>auto resizing of patient search list
 * <br>
 * <br>Revision 1.2  2013-11-15 10:07:52  ferring
 * <br>Sizing of the table now depends on the column, not on the column index
 * <br>
 * <br>Revision 1.1  2013-11-14 16:52:13  ferring
 * <br>Auto resizing of table columns
 * <br>
 */

public class AutoResizeTable extends JTable
{
	/* ======================================== */
	// CONSTANTS
	/* ======================================== */
	
	private static final long	serialVersionUID	= 1L;
	
	public static final int SIZE_TYPE_FIX			= 0;
	public static final int SIZE_TYPE_PROPORTIONAL	= 1;
	public static final int SIZE_TYPE_AS_NEEDED		= 2;
	public static final int SIZE_TYPE_AS_IS			= 3;
	
	
	
	/* ======================================== */
	// MEMBERS
	/* ======================================== */
	
	private Map<TableColumn, SizeValue>	sizeValues;
	
	private boolean					pauseResizing;
	
	
	
	/* ======================================== */
	// CONSTRUCTORS
	/* ======================================== */
	
	public AutoResizeTable () {}
	
	
	public AutoResizeTable (TableModel model)
	{
		super(model);
	}
	
	
	/* ======================================== */
	// OVERRIDDEN METHODS
	/* ======================================== */
	
	@Override
	public void setVisible (boolean aFlag)
	{
		super.setVisible(aFlag);
		resize();
	}
	
	
	@Override
	public void setSize (int width, int height)
	{
		super.setSize(width, height);
		resize();
	}
	
	
	@Override
	public void setSize (Dimension d)
	{
		super.setSize(d);
		resize();
	}
	
	
	@Override
	public void tableChanged (TableModelEvent e)
	{
		super.tableChanged(e);
		
		int			column		= e.getColumn();
		TableColumn	tableColumn;
		SizeValue	sv;
		
		
		// check the conditions and if 
		if (sizeValues == null)
		{
			 // no size values set
			return;
		}
		else if (column == -1)
		{
			// this means all columns have changed -> notify all
			pauseResizing = true;
			Enumeration<TableColumn> columns = getColumnModel().getColumns();
			while (columns.hasMoreElements())
			{
				tableColumn	= columns.nextElement();
				sv			= sizeValues.get(tableColumn);
				
				if (sv != null && sv.type == SIZE_TYPE_AS_NEEDED)
					tableChanged(new TableModelEvent((TableModel) e.getSource(), 
							e.getFirstRow(), e.getLastRow(), columnModel.getColumnIndex(tableColumn.getIdentifier()), e.getType()));
			}
			pauseResizing = false;
			resize();
			return;
		}
		else if (sizeValues.get(columnModel.getColumn(column)).type != SIZE_TYPE_AS_NEEDED)
		{
			// the sizing of this column is not defined or is not defined as "as needed"
			return;
		}
		
		// read the properties
		tableColumn		= columnModel.getColumn(column);
		sv				= sizeValues.get(tableColumn);
		int	startRow	= e.getFirstRow();
		int endRow		= e.getLastRow();
		
		// check and change properties and the width array for this column
		if (sv.widths == null 
				|| sv.widths.length != getRowCount())
		{
			sv.widths	= new int[getRowCount()];
			startRow	= 0;
			endRow		= getRowCount() - 1;
		}
		else
		{
			if (startRow > endRow)
			{
				int tmp		= startRow;
				startRow	= endRow;
				endRow		= tmp;
			}
			if (startRow >= sv.widths.length)
				startRow = sv.widths.length == 0 ? 0 : sv.widths.length-1;
			if (endRow >= sv.widths.length)
				endRow = sv.widths.length - 1;
		}
		
		// start calculating the sizes for the changed rows of this column
		TableCellRenderer	headerRenderer	= tableColumn.getHeaderRenderer();
		TableCellRenderer	cellRenderer	= tableColumn.getCellRenderer();
		
		// get the preferred header width
		if (headerRenderer == null)
		{
//			sv.headerWidth = GECAMedUtils.getStringSize(defaultFont, 
//					String.valueOf(tableColumn.getHeaderValue()));
			sv.headerWidth = new JLabel(String.valueOf(
					tableColumn.getHeaderValue())).getPreferredSize().width;
		}
		else
		{
			sv.headerWidth	= headerRenderer.getTableCellRendererComponent(this, tableColumn.getHeaderValue(), 
					false, false, TableModelEvent.HEADER_ROW, column).getPreferredSize().width;
		}
		
		// get the preferred width of the changed cells
		if (cellRenderer == null)
		{
			for (int index = startRow; index <= endRow; index++)
			{
				sv.widths[index] = new JLabel(String.valueOf(getValueAt(index, column)))
						.getPreferredSize().width;
			}
		}
		else
		{
			int[] selection = getSelectedColumns();
			
			for (int index = startRow; index <= endRow; index++)
			{
				sv.widths[index] = cellRenderer.getTableCellRendererComponent(this, getValueAt(index, column), 
						contains(selection, index), false, index, column).getPreferredSize().width;
			}
		}
		
		resize();
	}
	
	
	
	/* ======================================== */
	// CLASS BODY
	/* ======================================== */
	
	public void setSizeAsNeeded (int currentColumnIndex, int maxWidth)
	{
		setSizeAsNeeded(columnModel.getColumn(currentColumnIndex), maxWidth);
	}
	
	
	public void setSizeAsNeeded (TableColumn column, int maxWidth)
	{
		setSizing(column, SIZE_TYPE_AS_NEEDED, maxWidth);
	}

	
	public void setSizeAsIs (int currentColumnIndex)
	{
		setSizeAsIs(columnModel.getColumn(currentColumnIndex));
	}
	
	
	public void setSizeAsIs (TableColumn column)
	{
		setSizing(column, SIZE_TYPE_AS_IS, 0);
	}
	

	public void setSizing (int currentColumnIndex, int sizeType, int value)
	{
		setSizing(columnModel.getColumn(currentColumnIndex), sizeType, value);
	}
	
	
	public void setSizing (TableColumn column, int sizeType, int value)
	{
		// make sure the array has the correct size
		if (sizeValues == null)
			sizeValues = new HashMap<TableColumn, AutoResizeTable.SizeValue>(getColumnCount());
		
		SizeValue sv = sizeValues.get(column);
		if (sv == null)
			sv = new SizeValue();
		
		sv.type					= sizeType;
		sv.value				= value;
		sizeValues.put(column, sv);
	}
	
	
	
	/* ======================================== */
	// HELP METHODS
	/* ======================================== */
	
	private boolean contains (int[] array, int value)
	{
		for (int i : array)
			if (i == value)
				return true;
		return false;
	}
	
	
	private void resize ()
	{
		if (pauseResizing)
			return;
		
		// resize the column
		int[]	widths		= new int[getColumnCount()];
		int		tableWidth	= getSize().width;
		int		totalParts	= 0;
		int		totalWidth	= 0;
		int		partWidht;
		
		TableColumn			column;
		SizeValue			sv;
		
		
		for (int index = 0; index < getColumnCount(); index++)
		{
			column	= columnModel.getColumn(index);
			sv		= sizeValues.get(column);
			
			if (sv == null)
			{
				sv		= new SizeValue();
				sv.type	= SIZE_TYPE_AS_IS;
				sizeValues.put(column, sv);
			}
			
			switch (sv.type)
			{
				case SIZE_TYPE_AS_NEEDED:
					
					widths[index] = sv.getMaxWidth() + 2;
					break;
					
				case SIZE_TYPE_FIX:
					
					widths[index] = sv.value;
					break;
					
				case SIZE_TYPE_PROPORTIONAL:
					
					widths[index]	= 0;
					totalParts		+= sv.value;
					break;
					
				case SIZE_TYPE_AS_IS:
				default:
					
					widths[index] = column.getWidth();
					break;
			}
			totalWidth	+= widths[index];
		}
		
		if (totalParts > 0)
			partWidht = (tableWidth - totalWidth) / totalParts;
		else
			partWidht = 0;
		
		int columnIndex;
		for (TableColumn tableColumn : sizeValues.keySet())
		{
			columnIndex	= columnModel.getColumnIndex(tableColumn.getIdentifier());
			
			// calculate the proportional widths
			sv = sizeValues.get(tableColumn);
			if (sv.type == SIZE_TYPE_PROPORTIONAL)
			{
				widths[columnIndex] = sv.value * partWidht;
			}
			
			// set the widths of the columns
			tableColumn.setWidth(widths[columnIndex]);
			tableColumn.setPreferredWidth(widths[columnIndex]);
		}
	}
	
	
	
	/* ======================================== */
	// CLASS: 
	/* ======================================== */
	
	private class SizeValue
	{
		public int		value;
		public int		type;
		public int[]	widths;
		public int		headerWidth;
		
		
		public int getMaxWidth ()
		{
			int max = headerWidth+8;
			
			if (widths == null)
				return max;
			
			for (int width : widths)
			{
				if (max < width)
					max = width;
				if (value >= 0 && max > value)
					return value;
			}
			
			return max;
		}
	}
}
