/*
 * 作成日: 2006/04/30
 */
package net.sourceforge.swingx.jspread;

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

import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;

import net.sourceforge.swingx.SxBevelBorder;


/**
 * セル・レンダラーに必要なメソッドを定義します.
 * 内部にいくつかのクラスを定義しています。
 * @author Asan
 */
public interface JSpreadCellRenderer {

	/**
	 * セルを描画するのに使用されるコンポーネントを返します.
	 * @param	spread	スプレッド(null不可)
	 * @param	value	値(null可)
	 * @param	isSelected	セルが選択されているかどうか？
	 * @param	hasFocus	セルにフォーカスがあるかどうか？
	 * @param	type		CELL/ROW/COLUMN/CORNER
	 * @param	rowDataIndex	列データ番号(dataIndex)(0..)
	 * @param	colDataIndex	行データ番号(dataIndex)(0..)
	 * @return	セルを描画するのに使用するコンポーネント(null不可)
	 */ 
	JComponent getSpreadRendererComponent(JSpread spread, Object value, 
			boolean isSelected, boolean hasFocus, 
			int type, int rowDataIndex, int colDataIndex);

	
	/**
	 * デフォルトのセル・レンダラーです.
	 * javax.swing.JLabel を使って描画します。
	 * @author Asan
	 */
	public static class DefaultCellRenderer extends JLabel
				implements JSpreadCellRenderer {
	    protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
	    protected static Border bevelBorder = new SxBevelBorder(BevelBorder.RAISED);
	    /**
	     * デフォルトのセル・レンダラーを作成します.
	     */
	    public DefaultCellRenderer() {
			super();
			setOpaque(true);
			setBorder(noFocusBorder);
		}
		/**
		 * セルを描画するのに使用されるコンポーネントを返します.
		 * @param	spread	スプレッド(null不可)
		 * @param	value	値(null可)
		 * @param	isSelected	セルが選択されているかどうか？
		 * @param	hasFocus	セルにフォーカスがあるかどうか？
		 * @param	type		CELL/ROW/COLUMN/CORNER
		 * @param	rowDataIndex	列データ番号(dataIndex)(0..)
		 * @param	colDataIndex	行データ番号(dataIndex)(0..)
		 * @return	セルを描画するのに使用するコンポーネント(null不可)
		 */ 
		public JComponent getSpreadRendererComponent(JSpread spread, Object value, 
				boolean isSelected, boolean hasFocus, 
				int type, int rowDataIndex, int colDataIndex) {
			switch (type) {
			case JSpread.CELL:
				return getSpreadCellRendererComponent(spread, value, isSelected, hasFocus, rowDataIndex, colDataIndex);
			case JSpread.ROW:
			case JSpread.COLUMN:
			case JSpread.CORNER:
				return getSpreadHeaderRendererComponent(spread, value, 
						isSelected, hasFocus, type, rowDataIndex, colDataIndex);
			}
			return this;
		}
		public JComponent getSpreadCellRendererComponent(JSpread spread, Object value, 
				boolean isSelected, boolean hasFocus, 
				int rowDataIndex, int colDataIndex) {
			if (isSelected) {
				super.setForeground(spread.properties.getColor(JSpread.CELL_SELECT_FOREGROUND_COLOR));
				super.setBackground(spread.properties.getColor(JSpread.CELL_SELECT_BACKGROUND_COLOR));
			} else {
				super.setForeground(spread.properties.getColor(JSpread.CELL_FOREGROUND_COLOR));
				super.setBackground(spread.properties.getColor(JSpread.CELL_BACKGROUND_COLOR));
			}

			setFont(spread.getFont());

			if (hasFocus) {
				setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
				if (!isSelected && spread.isCellEditable(rowDataIndex, colDataIndex)) {
					Color col;
					col = UIManager.getColor("Table.focusCellForeground");
					if (col != null) {
						super.setForeground(col);
					}
					col = UIManager.getColor("Table.focusCellBackground");
					if (col != null) {
						super.setBackground(col);
					}
				}
			} else {
				setBorder(noFocusBorder);
			}
			setHorizontalAlignment(JLabel.LEFT);
			setValue(value);
			return this;
		}

		public JComponent getSpreadHeaderRendererComponent(JSpread spread, Object value, 
				boolean isSelected, boolean hasFocus, 
				int type, int rowDataIndex, int colDataIndex) {
			if (isSelected) {
				super.setForeground(spread.properties.getColor(JSpread.HEADER_SELECT_FOREGROUND_COLOR));
				super.setBackground(spread.properties.getColor(JSpread.HEADER_SELECT_BACKGROUND_COLOR));
			} else {
				super.setForeground(spread.properties.getColor(JSpread.HEADER_FOREGROUND_COLOR));
				super.setBackground(spread.properties.getColor(JSpread.HEADER_BACKGROUND_COLOR));
			}

			setFont(spread.getFont());

			if (hasFocus) {
				setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
				if (!isSelected && spread.isCellEditable(rowDataIndex, colDataIndex)) {
					Color col;
					col = UIManager.getColor("Table.focusCellForeground");
					if (col != null) {
						super.setForeground(col);
					}
					col = UIManager.getColor("Table.focusCellBackground");
					if (col != null) {
						super.setBackground(col);
					}
				}
			} else {
				setBorder(bevelBorder);
			}
			setHorizontalAlignment(JLabel.CENTER);
			setValue(value);
			return this;
			
		}
		/**
		 * 値を設定します.
		 * 任意のオブジェクトを、javax.swing.JLabel のテキストに設定します.
		 * @param value	セルに表示する値(null可)
		 */
		protected void setValue(Object value) {
			setText((value == null) ? "" : value.toString());
		}
	}

    /**
     * java.lang.Number から継承したオブジェクトで使用するレンダラーです。
     * Byte, Integer, Long, Short, BigInteger などで使います.
     * JSpreadDefaultCellRendererから継承し、右詰めで表示します。
     * Double, Float, BigDecimalはこのクラスから継承したDoubleRendererが使われます。
     * @author Asan 
    */
    public static class NumberRenderer extends DefaultCellRenderer {
		public NumberRenderer() {
			super();
		}
		public JComponent getSpreadCellRendererComponent(JSpread spread, Object value, 
				boolean isSelected, boolean hasFocus, 
				int rowDataIndex, int colDataIndex) {
			JLabel comp = (JLabel) super.getSpreadCellRendererComponent(spread, value, 
					isSelected, hasFocus, 
					rowDataIndex, colDataIndex);
			comp.setHorizontalAlignment(JLabel.RIGHT);
			return comp;
		}
    }

    /**
     * Icon のセル・レンダラです.
     * Iconインターフェースを実装しているオブジェクトに対して使用します。
     * @author Asan 
     */
    public static class IconRenderer extends DefaultCellRenderer {
		public IconRenderer() {
			super();
		}
		public JComponent getSpreadCellRendererComponent(JSpread spread, Object value, 
				boolean isSelected, boolean hasFocus, 
				int rowDataIndex, int colDataIndex) {
			JLabel comp = (JLabel) super.getSpreadCellRendererComponent(spread, value, 
					isSelected, hasFocus, 
					rowDataIndex, colDataIndex);
			comp.setHorizontalAlignment(JLabel.CENTER);
			return comp;
		}

		/**
		 * 値を設定します.
		 * 任意のオブジェクトを、JLabelのアイコンに設定します.
		 * @param value	セルに表示する値(null可)
		 */
		public void setValue(Object value) {
			setIcon((value instanceof Icon) ? (Icon) value : null);
		}
	}
    
    /**
     * Date のセル・レンダラです.
      * @author Asan 
     */
    public static class DateRenderer extends DefaultCellRenderer {
		protected DateFormat formatter = null;

		public DateRenderer() {
			super();
		}
		/**
		 * 日時フォーマットを指定します。このメソッドが呼ばれなければ、デフォルトの日時フォーマット
		 * <code> DateFormat.getInstance() </code>が使用されます。
		 * @param formatter	日時フォーマット(null不可)
		 */
		public void setFormat(DateFormat formatter) {
			assert formatter != null;
			this.formatter = formatter;
		}
		/**
		 * 値を設定します.
		 * 任意のオブジェクトを、JLabelのアイコンに設定します.
		 * @param value	セルに表示する値(null可)
		 */
		public void setValue(Object value) {
			if (formatter == null) {
				formatter = DateFormat.getDateInstance();
			}
			setText((value == null) ? "" : formatter.format(value));
		}
	}
    
    /**
     * Boolean のセル・レンダラです.
     * javax.swing.JCheckBoxを使って描画します.
     * @author Asan
     */
    public static class BooleanRenderer extends JCheckBox implements JSpreadCellRenderer {
		private static final Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);

		public BooleanRenderer() {
			super();
			setBorderPainted(true);
		}

		/**
		 * セルを描画するのに使用されるコンポーネントを返します.
		 * @param	spread	スプレッド(null不可)
		 * @param	value	値(null可)
		 * @param	isSelected	セルが選択されているかどうか？
		 * @param	hasFocus	セルにフォーカスがあるかどうか？
		 * @param	type		CELL/ROW/COLUMN/CORNER
		 * @param	rowDataIndex	列データ番号(dataIndex)(0..)
		 * @param	colDataIndex	行データ番号(dataIndex)(0..)
		 * @return	セルを描画するのに使用するコンポーネント(null不可)
		 */ 
		public JComponent getSpreadRendererComponent(JSpread spread,
				Object value, boolean isSelected, boolean hasFocus,
				int type, int rowDataIndex, int colDataIndex) {
			if (isSelected) {
				setForeground(spread.properties.getColor(JSpread.CELL_SELECT_FOREGROUND_COLOR));
				setBackground(spread.properties.getColor(JSpread.CELL_SELECT_BACKGROUND_COLOR));
			} else {
				setForeground(spread.properties.getColor(JSpread.CELL_FOREGROUND_COLOR));
				setBackground(spread.properties.getColor(JSpread.CELL_BACKGROUND_COLOR));
			}
			setSelected((value != null && ((Boolean) value).booleanValue()));

			if (hasFocus) {
				setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
			} else {
				setBorder(noFocusBorder);
			}
			setHorizontalAlignment(JLabel.CENTER);

			return this;
		}
	}
    
    /**
     * java.awt.Color のセル・レンダラです.
     * javax.swing.JLabelのアイコンにその色を、ラベルにRGB値を表示します。
     * @author Asan
     */
	public static class ColorRenderer extends DefaultCellRenderer {
		/** 色のアイコンの幅です. */
		public static final int ICON_WIDTH = 15;
		/** 色のアイコンの高さです. */
		public static final int ICON_HEIGHT = 15;
		/**
		 * 値を設定します.
		 * 任意のオブジェクトを、JLabelのアイコンに設定します.
		 * @param value	セルに表示する値(null可)
		 */
		public void setValue(Object value) {
			if (value instanceof Color) {
				Color color = (Color) value;
				this.setText(""+color.getRed()+" "+color.getGreen()+" "+color.getBlue());
				System.out.println("Font.size="+getFont().getSize());
				
				this.setIcon(new ColorIcon(color, ICON_WIDTH, getFont().getSize()));
			} else {
				this.setText("");
				this.setIcon(new ColorIcon(null, ICON_WIDTH, getFont().getSize()));
			}
		}
		static class ColorIcon implements Icon {
			protected Color color;
			protected int width, height;
			public ColorIcon(Color color, int w, int h) {
				this.color = color;
				width = w;
				height = h;
			}
			public int getIconWidth() { return width; }
			public int getIconHeight() { return height; }
			public void paintIcon(Component c, Graphics g, int x, int y) {
				if (color != null) {
					g.setColor(color);
					g.fillRect(x, y, width, height);
					g.setColor(Color.BLACK);
					g.drawRect(x, y, width, height);	
				}
				else {	// nullのときには、枠のみ
					g.setColor(Color.white);
					g.fillRect(x, y, width, height);
					g.setColor(Color.black);
					g.drawRect(x, y, width, height);
				}
			}
		}
	}	
}
