package de.muntjak.tinylookandfeel.table;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;

import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.UIResource;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

import de.muntjak.tinylookandfeel.Theme;
import de.muntjak.tinylookandfeel.TinyTableHeaderUI;
import de.muntjak.tinylookandfeel.controlpanel.ColorRoutines;

/**
 * This TableCellRenderer is installed on the table header
 * in TinyTableHeaderUI.installDefaults() so we can change the
 * border on rollover and show an arrow icon for sorted columns.
 * @author Hans Bickel
 *
 */
public class TinyTableHeaderRenderer extends DefaultTableCellRenderer implements UIResource {

	private static final Icon arrowNo = new Arrow(true, -1);
	
	// arrows array will be lazily filled
	private static final Icon[][] arrows = new Icon[2][4];
	
	private int[] horizontalAlignments;
	
	public TinyTableHeaderRenderer() {
		setHorizontalAlignment(CENTER);
		setHorizontalTextPosition(LEFT);
	}
	
	/**
	 * Sets horizontal alignments of renderers where an index
	 * in the argument array corresponds to a column index.
	 * @param alignments array of the following constants
     *           defined in <code>SwingConstants</code>:
     *           <code>LEFT</code>,
     *           <code>CENTER</code>,
     *           <code>RIGHT</code>,
     *           <code>LEADING</code> (the default for text-only labels) or
     *           <code>TRAILING</code>.
	 */
	public void setHorizontalAlignments(int[] alignments) {
		horizontalAlignments = alignments;
	}

	public Component getTableCellRendererComponent(JTable table, Object value,
        boolean isSelected, boolean hasFocus, int row, int column)
	{
		LookAndFeel laf = UIManager.getLookAndFeel();

		if(laf == null || !"TinyLookAndFeel".equals(laf.getName())) {
			if(table != null) {
				JTableHeader header = table.getTableHeader();
				
				if(header != null) {
					setBackground(header.getBackground());
				    setForeground(header.getForeground());
				    setFont(header.getFont());
				}
			}
			
			setIcon(null);
			setText((value == null) ? "" : value.toString());
			setBorder(UIManager.getBorder("TableHeader.cellBorder"));
			
			return this;
		}
		
		boolean isRolloverColumn = false;
		Icon icon = arrowNo;
		int sortingDirection = SortableTableData.SORT_ASCENDING;
		int priority = -1;
		boolean paintArrow = false;

		if(table != null) {
			JTableHeader header = table.getTableHeader();
			
			if(header != null) {
				Object o = header.getClientProperty(TinyTableHeaderUI.ROLLOVER_COLUMN_KEY);
				
				if(o != null) {
					int rolloverColumn = ((Integer)o).intValue();
					
					if(rolloverColumn == column) {
						isRolloverColumn = true;
					}
				}
				
				o = header.getClientProperty(TinyTableHeaderUI.SORTED_COLUMN_KEY);
				
				if(o != null) {
					int sc[] = (int[])o;
					priority = -1;
					
					for(int i = 0; i < sc.length; i++) {
						if(sc[i] == column) {
							priority = i;
						}
					}
					
					if(priority > -1) {
						paintArrow = true;
						o = header.getClientProperty(TinyTableHeaderUI.SORTING_DIRECTION_KEY);
						
						if(o != null) {
							int[] sd = (int[])o;
							sortingDirection = sd[priority];
						}
					}
				}

			    if(isRolloverColumn) {
			    	setBackground(Theme.tableHeaderRolloverBackColor[Theme.style].getColor());
			    }
			    else {
			    	setBackground(header.getBackground());
			    }
			    
			    setForeground(header.getForeground());
			    setFont(header.getFont());
			}
			
			TableModel model = table.getModel();
			
			if(model instanceof SortableTableData) {
				if(paintArrow) {
					int pri = Math.min(3, priority);
					
					if(sortingDirection == SortableTableData.SORT_ASCENDING) {
						if(arrows[0][pri] == null) {
							arrows[0][pri] = new Arrow(false, priority);
						}
						
						icon = arrows[0][pri];
					}
					else {
						if(arrows[1][pri] == null) {
							arrows[1][pri] = new Arrow(true, priority);
						}
						
						icon = arrows[1][pri];
					}
				}
				else {
					int modelColumn = table.getColumnModel().getColumn(column).getModelIndex();
					
					if(!((SortableTableData)model).isColumnSortable(modelColumn)) {
						icon = null;
					}
				}
			}
			else {
				icon = null;
				setToolTipText(null);
			}
		}

		setIcon(icon);
		setText((value == null) ? "" : value.toString());

		if(isRolloverColumn) {
			setBorder(UIManager.getBorder("TableHeader.cellRolloverBorder"));
	    }
	    else {
	    	setBorder(UIManager.getBorder("TableHeader.cellBorder"));
	    }
		
		int modelColumn = table.getColumnModel().getColumn(column).getModelIndex();
		
		if(horizontalAlignments != null && horizontalAlignments.length > modelColumn) {
			setHorizontalAlignment(horizontalAlignments[modelColumn]);
		}
		
		return this;
	}

	private static class Arrow implements Icon {

		private static final int height = 11;
		private boolean descending;
		private int priority;

		public Arrow(boolean descending, int priority) {
			this.descending = descending;
			this.priority = Math.min(3, priority);
		}

		public void paintIcon(Component c, Graphics g, int x, int y) {
			if(priority == -1) return;	// empty icon
			
			// In a compound sort, make each successive triangle
			// smaller than the previous one
			int dx = priority;
			int dy = (height - 5 + priority) / 2;

			g.translate(x + dx, y + dy);
			g.setColor(Theme.tableHeaderArrowColor[Theme.style].getColor());
			
			if(descending) {
				switch(priority) {
					case 0:
						g.drawLine(4, 4, 4, 4);
						g.drawLine(3, 3, 5, 3);
						g.drawLine(2, 2, 6, 2);
						g.drawLine(1, 1, 7, 1);
						g.drawLine(0, 0, 8, 0);
						break;
					case 1:
						g.drawLine(3, 3, 3, 3);
						g.drawLine(2, 2, 4, 2);
						g.drawLine(1, 1, 5, 1);
						g.drawLine(0, 0, 6, 0);
						break;
					case 2:
						g.drawLine(2, 2, 2, 2);
						g.drawLine(1, 1, 3, 1);
						g.drawLine(0, 0, 4, 0);
						break;
					case 3:
					default:
						g.drawLine(1, 1, 1, 1);
						g.drawLine(0, 0, 2, 0);
						break;
				}
			}
			else {
				switch(priority) {
					case 0:
						g.drawLine(4, 0, 4, 0);
						g.drawLine(3, 1, 5, 1);
						g.drawLine(2, 2, 6, 2);
						g.drawLine(1, 3, 7, 3);
						g.drawLine(0, 4, 8, 4);
						break;
					case 1:
						g.drawLine(3, 0, 3, 0);
						g.drawLine(2, 1, 4, 1);
						g.drawLine(1, 2, 5, 2);
						g.drawLine(0, 3, 6, 3);
						break;
					case 2:
						g.drawLine(2, 0, 2, 0);
						g.drawLine(1, 1, 3, 1);
						g.drawLine(0, 2, 4, 2);
						break;
					case 3:
					default:
						g.drawLine(1, 0, 1, 0);
						g.drawLine(0, 1, 2, 1);
						break;
				}
			}
			
			g.translate(-(x + dx), -(y + dy));
		}

		public int getIconWidth() {
			return 9;
		}

		public int getIconHeight() {
			return height;
		}
	}
}
