package de.muntjak.tinylookandfeel.controlpanel;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.Icon;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

public class NonSortableTableModel extends AbstractTableModel {

	public NonSortableTableModel() {
		this(false);
	}
	
	// data holds a vector of Record objects where
	// one Record represents one row of data
	protected Vector data;
	private Record template;
	private Vector columnNames;
	private boolean sortable;
	
	protected NonSortableTableModel(boolean sortable) {
		super();
		
		this.sortable = sortable;
		createData();
	}

	private void createData() {
		columnNames = new Vector();
		
		columnNames.add("C1");
		columnNames.add("C2");
		columnNames.add("C3");
		columnNames.add("C4");
		
		TableColorIcon.reset();
		template = null;
		data = new Vector();
		
		for(int i = 0; i < 50; i++) {
			Record rec = new Record(i + 1);
			
			data.add(rec);
			
			if(template == null) {
				template = rec;
			}
		}
	}

// AbstractTableModel implementation
	
	public int getColumnCount() {
		if(template == null) return 0;
		
		return template.getColumnCount();
	}

	public int getRowCount() {
		return data.size();
	}

	public Object getValueAt(int row, int column) {
		Record rec = (Record)data.get(row);
		
		return rec.getValueAt(column);
	}
	
	public Class getColumnClass(int column) {
		if(template == null) return Object.class;
		
		return template.getColumnClass(column);
	}
	
	public String getColumnName(int column) {
		return (String)columnNames.get(column);
	}
	
// Some methods for testing
	
	public void addColumn(Class cl, int column) {
		columnNames.add(column, "N" + (column + 1));
		
		Iterator ii = data.iterator();
		while(ii.hasNext()) {
			Record rec = (Record)ii.next();
			
			rec.addColumn(cl, column);
		}
		
		fireTableStructureChanged();
	}
	
	public void removeColumn(int column) {
		columnNames.remove(column);
		
		Iterator ii = data.iterator();
		while(ii.hasNext()) {
			Record rec = (Record)ii.next();
			
			rec.removeColumn(column);
		}
		
		fireTableStructureChanged();
	}
	
	public void removeAllRows() {
		data.clear();
		fireTableDataChanged();
	}
	
	public void createNewData() {
		createData();
		
		// might be that number of columns changed
		fireTableStructureChanged();
	}

	/**
	 * A Record represents one row of data.
	 * By default a Record consists of 4 values but
	 * columns can be added and can be removed.
	 * 
	 * @author Hans Bickel
	 *
	 */
	protected class Record {

		private Vector values;
		private int oldRow, newRow;
		
		protected Record(int index) {
			values = new Vector();
			Object v1 = new Integer(index);
			Object v2 = createRandomIcon();
			Object v3 = null;
			
			if(sortable) {
				if(index == 1) {
					v3 = "Sortable";
				}
				else if(index == 2) {
					v3 = "Table";
				}
				else if(index == 3) {
					v3 = "Data";
				}
				else {
					v3 = createRandomString();
				}
			}
			else {
				if(index == 1) {
					v3 = "Non-";
				}
				else if(index == 2) {
					v3 = "sortable";
				}
				else if(index == 3) {
					v3 = "Table";
				}
				else if(index == 4) {
					v3 = "Data";
				}
				else {
					v3 = createRandomString();
				}
			}
			
			Object v4 = createRandomInteger();
			
			values.add(v1);
			values.add(v2);
			values.add(v3);
			values.add(v4);
		}
		
		public Object getValueAt(int column) {
			if(column < 0 || column >= values.size()) return null;
			
			return values.get(column);
		}
		
		public Class getColumnClass(int column) {
			if(column < 0 || column >= values.size()) return Object.class;
			
			return values.get(column).getClass();
		}
		
		public void addColumn(Class cl, int column) {
			if(Double.class.equals(cl)) {
				values.add(column, createRandomDouble());
			}
			else if(Icon.class.equals(cl)) {
				values.add(column, createRandomIcon());
			}
			else if(Integer.class.equals(cl)) {
				values.add(column, createRandomInteger());
			}
			else if(String.class.equals(cl)) {
				values.add(column, createRandomString());
			}
		}
		
		public void removeColumn(int column) {
			values.remove(column);
		}
		
		public int getColumnCount() {
			return values.size();
		}

		private String createRandomString() {
			// return a string of 3 random characters
			char[] a = new char[3];
			
			for(int i = 0; i < a.length; i++) {
				a[i] = createRandomChar();
			}

			return new String(a);
		}
		
		private char createRandomChar() {
			// return one uppercase letter
			return (char)('A' + (char)(Math.random() * 3));
		}

		private Double createRandomDouble() {
			// return a Double between 0.00 and 99.00
			int x = (int)(Math.random() * 10000);
			return new Double(x / 100.0);
		}
		
		private Integer createRandomInteger() {
			// return an Integer between 1 and 20
			return new Integer((int)(Math.random() * 20) + 1);
		}
		
		private Icon createRandomIcon() {
			return new TableColorIcon();
		}

		
		public int getNewRow() {
			return newRow;
		}

		
		public void setNewRow(int newRow) {
			this.newRow = newRow;
		}

		
		public int getOldRow() {
			return oldRow;
		}

		
		public void setOldRow(int oldRow) {
			this.oldRow = oldRow;
		}
	}
	
	/**
	 * A 24x12 sized icon of random color.
	 */
	public static final class TableColorIcon implements Icon {
		
		private Color color;
		private static int hue = 0;
		
		TableColorIcon() {
			color = Color.getHSBColor((float)(hue / 360.0), 0.5f, 0.9f);
			hue += 360 / 20;
		}
		
		static void reset() {
			hue = 0;
		}

		public void paintIcon(Component c, Graphics g, int x, int y) {
			g.setColor(Color.DARK_GRAY);
			g.drawRect(x, y, getIconWidth() - 1, getIconHeight() - 1);
			
			g.setColor(color);
			g.fillRect(x + 1, y + 1, getIconWidth() - 2, getIconHeight() - 2);
		}

		public int getIconWidth() {
			return 24;
		}

		public int getIconHeight() {
			return 12;
		}

		public int compareTo(Object o) {
			if(o == null) return 1;
			if(!(o instanceof TableColorIcon)) return 1;
			
			Color c2 = ((TableColorIcon)o).color;
			
			if(color.getRGB() == c2.getRGB()) {
				return 0;
			}
			else if(color.getRGB() > c2.getRGB()) {
				return 1;
			}
			
			return -1;
		}
	}
}
