package net.sourceforge.swingx.jspread;

import java.awt.*;
import java.awt.event.MouseEvent;

import javax.swing.*;

import net.sourceforge.swingx.Logger;

class JSpreadCellPane extends JPanel {
	private static Logger logger = Logger.getLogger(JSpreadCellPane.class);

	transient final JSpread spread;

	/** 自分のパネルの位置. 0 or 1 */
	int rowPaneIndex;

	/** 自分のパネルの位置. 0 or 1 */
	int colPaneIndex;

	/** セルレンダラーを行うクラス. */
	CellRendererPane rendererPane = new CellRendererPane();

	JSpreadCellPane(JSpread spread, int row, int col) {
		assert spread != null;
		assert row == 0 || row == 1 : row;
		assert col == 0 || col == 1 : col;
		this.spread = spread;
		this.rowPaneIndex = row;
		this.colPaneIndex = col;
		this.setLayout(null);
		this.add(rendererPane);
		JSpreadCellListener listener = new JSpreadCellListener(spread, this);
		addMouseListener(listener);
		addMouseMotionListener(listener);
		enableEvents(AWTEvent.KEY_EVENT_MASK);
		addKeyListener(listener);
		setFocusable(true);
		// requestFocusInWindow();
		//setToolTipText("UseToolTip");// TODO:ToolTipsを利用者が使えるようにすること
		setCursor(spread.properties.getCursor(JSpread.CELL_CURSOR));
	}

	/** 最適なサイズを返す */
	public Dimension getPreferredSize() {
		JSpreadSheet sheet = spread.getSheet();
		int totalW = sheet.colHeader.getTotalWidth();
		int totalH = sheet.rowHeader.getTotalWidth();
		return new Dimension(totalW, totalH);
	}

	public void paintComponent(Graphics g) {
		logger.debug("enter");
		Graphics2D g2 = (Graphics2D) g;
		// 背景を表示する
		int width = getWidth();
		int height = getHeight();
		g2.setPaint(spread.properties.getColor(JSpread.BACKGROUND_COLOR));
		g2.fillRect(0, 0, width, height);

		// 必要な情報を集める
		Rectangle rect = g2.getClipBounds();
		//System.out.println(rect);
		JSpreadSheet sheet = spread.getSheet();
		int startCol = spread.getSheet().colHeader.getViewPositionAtPoint(rect.x);
		int startRow = spread.getSheet().rowHeader.getViewPositionAtPoint(rect.y);
		if (startCol == -1 || startRow == -1) return;	// セル外の再描画
		int endCol = spread.getSheet().colHeader.getViewPositionAtPoint(rect.x+rect.width);
		int endRow = spread.getSheet().rowHeader.getViewPositionAtPoint(rect.y+rect.height);
		if (endRow == -1) endRow = spread.getSheet().rowHeader.getViewCount() - 1;
		if (endCol == -1) endCol = spread.getSheet().colHeader.getViewCount() - 1;
		Rectangle viewportRect = g.getClipBounds();
		//System.out.println("viewportRect=" + viewportRect);

		Rectangle cellRect = new Rectangle();
		Color horizontalGridColor = spread.properties
				.getColor(JSpread.HORIZONTAL_GRID_COLOR);
		Color verticalGridColor = spread.properties
				.getColor(JSpread.VERTICAL_GRID_COLOR);

		// セルを表示する
		cellRect.y = sheet.rowHeader.getPoint(startRow);
		for (int r = startRow; r <= endRow; r++) {
			int rowDataIndex = sheet.rowHeader.getDataIndex(r);
			cellRect.height = sheet.rowHeader.getWidth(rowDataIndex);
			cellRect.x = sheet.colHeader.getPoint(startCol);
			for (int c = startCol; c <=endCol; c++) {
				int colDataIndex = sheet.colHeader.getDataIndex(c);
				cellRect.width = sheet.colHeader.getWidth(colDataIndex);
				// セルの値と状態を取得
				Object value = spread.getValueAt(rowDataIndex, colDataIndex);
				boolean isSelected = sheet.getSelection(r, c);
				boolean hasFocus = sheet.focusCol == c && sheet.focusRow == r;
				// レンダラーを取得
				if (value instanceof JComponent) {
					JComponent comp = (JComponent) value;
					add(comp);
					comp.setBounds(cellRect);
				} else {
					JSpreadCellRenderer renderer = spread.getCellRenderer(value, rowDataIndex, colDataIndex);
					JComponent comp = renderer.getSpreadRendererComponent(
							spread, value, isSelected, hasFocus, 
							JSpread.CELL, rowDataIndex, colDataIndex);
					rendererPane.paintComponent(g2, comp, this, cellRect.x,
							cellRect.y, cellRect.width, cellRect.height, true);
				}
				// 次のセルへ
				cellRect.x += cellRect.width + (verticalGridColor != null ? 1 : 0);
			}
			// 次のセルへ
			cellRect.y += cellRect.height + (horizontalGridColor != null ? 1 : 0);
		}
		int max_cell_x = cellRect.x;
		int max_cell_y = cellRect.y;

		// 横のグリッドを表示する
		if (horizontalGridColor != null) {
			g2.setPaint(horizontalGridColor);
			int total = sheet.rowHeader.getPoint(startRow) - 1;
			for (int r = startRow; r <=endRow; r++) {
				int row_index = sheet.rowHeader.getDataIndex(r);
				total += sheet.rowHeader.getWidth(row_index) + 1;
				g2.drawLine(0, total, max_cell_x, total);
				if (total > viewportRect.getMaxY())
					break;
			}
		}

		// 縦のグリッドを表示する
		if (verticalGridColor != null) {
			g2.setPaint(verticalGridColor);
			int total = sheet.colHeader.getPoint(startCol) - 1;
			for (int c = startCol; c <= endCol; c++) {
				int col_index = sheet.colHeader.getDataIndex(c);
				total += sheet.colHeader.getWidth(col_index) + 1;
				g2.drawLine(total, 0, total, max_cell_y);
				if (total > viewportRect.getMaxX())
					break;
			}
		}

		// フォーカスを表示する
		if (sheet.colHeader.getViewCount() != 0
				&& sheet.rowHeader.getViewCount() != 0) {
			int minRow = Math.min(sheet.focusRow, sheet.curRow);
			int maxRow = Math.max(sheet.focusRow, sheet.curRow);
			int minCol = Math.min(sheet.focusCol, sheet.curCol);
			int maxCol = Math.max(sheet.focusCol, sheet.curCol);
			if (sheet.curCol == -1) {
				minCol = 0;
				maxCol = sheet.colHeader.getViewCount() - 1;
			}
			if (sheet.curRow == -1) {
				minRow = 0;
				maxRow = sheet.rowHeader.getViewCount() - 1;
			}
			int x = sheet.colHeader.getPoint(minCol);
			int y = sheet.rowHeader.getPoint(minRow);
			int w = sheet.colHeader.getTailPoint(maxCol) - x;
			int h = sheet.rowHeader.getTailPoint(maxRow) - y;
			g2.setPaint(spread.properties.getColor(JSpread.CELL_FOCUS_COLOR));
			g2.setStroke(spread.properties.getStroke(JSpread.CELL_FOCUS_STROKE));
			g2.drawRect(x, y, w, h);
		}

//		System.out.println(
//				"row="+startRow+".."+endRow+" col="+startCol+".."+endCol+" "+
//			(System.currentTimeMillis() - start)+"msec");
		logger.debug("exit");
	}
	public String getToolTipText(MouseEvent event) {
		return spread.getToolTipText(getName(), event);
	}
	//////////// デバッグ用
	public void setSize(Dimension dim) {
		logger.debug("enter "+dim);
		super.setSize(dim);
		logger.debug("exit");
	}
	public void setSize(int w, int h) {
		logger.debug("enter "+w+" "+h);
		super.setSize(w, h);
		logger.debug("exit");
	}
	public void setBounds(int x, int y, int width, int height) {
		logger.debug("enter "+x+" "+y+" "+width+" "+height);
		super.setBounds(x, y, width, height);
		logger.debug("exit");
	}
	public void setBounds(Rectangle r) {
		logger.debug("enter "+r.x+" "+r.y+" "+r.width+" "+r.height);
		super.setBounds(r);
		logger.debug("exit");
	}
}
