package net.sourceforge.swingx.jspread;

import java.awt.Color;
import java.util.ArrayList;

/**
 * 両方のヘッダのデータを管理します.
 * 
 * @author Asan
 */
public class JSpreadHeader implements JSpreadConstant {
	final JSpread spread;
	/** 向き. VERTICAL/HORIZONTALのいずれか */
	final int orientation;
	/** 太さ(dot) 0以上 */
	int thickness;
	int defaultWidth;
	/** 各列の情報. ArrayList<HeaderInfo> */
	ArrayList infos = new ArrayList();
	/**
	 * テーブルが幅のサイズを自動変更して各列でテーブル幅全体を占めるようにするか
	 * どうかを指定し、そのサイズ変更の方法を指定します。
	 * デフォルトは<code> AUTO_RESIZE_OFF </code>です。
	 */
	int autoResizeMode;
	/** ヘッダの幅をリサイズしているときのindex.リサイズしていないときは、-1 */
//	int resizingViewIndex;
	transient int[] dataIndexList;
	
	JSpreadHeader(JSpread spread, int orientation, int count, int thickness, int defaultWidth) {
		assert spread != null;
		assert orientation == JSpread.VERTICAL || orientation == JSpread.HORIZONTAL: orientation;
		assert count >= 0: count;
		assert thickness >= 0: thickness;
		assert defaultWidth >= 0: defaultWidth;
		this.spread = spread;
		this.orientation = orientation;
		this.thickness = thickness;
		this.defaultWidth = defaultWidth;
		this.autoResizeMode = JSpread.AUTO_RESIZE_OFF;
//		this.resizingViewIndex = -1;
		for (int i=0; i<count; i++) {
			infos.add(new HeaderInfo(null, defaultWidth, i));
		}
		makeDataIndexList();
	}
	/**
	 * ヘッダ情報を取得する. 
	 * @param dataIndex	
	　* @return	ヘッダ情報
	 */
	HeaderInfo getInfo(int dataIndex) {
		assert 0 <= dataIndex && dataIndex < infos.size(): dataIndex;
		return (HeaderInfo) infos.get(dataIndex);
	}
	/**
	 * ヘッダの幅(dot)を求める.
	 * @param dataIndex	データのindex。0..
	 * @return	ヘッダの幅(dot)
	 */
	public int getWidth(int dataIndex) {
		assert 0 <= dataIndex && dataIndex < infos.size(): dataIndex;
		return getInfo(dataIndex).width;
	}
	public void setWidth(int dataIndex, int width) {
		assert 0 <= dataIndex && dataIndex < infos.size(): dataIndex;
		getInfo(dataIndex).setWidth(width);
	}
	int getMinWidth(int dataIndex) {
		assert 0 <= dataIndex && dataIndex < infos.size(): dataIndex;
		return getInfo(dataIndex).minWidth;
	}
	int getMaxWidth(int dataIndex) {
		assert 0 <= dataIndex && dataIndex < infos.size(): dataIndex;
		return getInfo(dataIndex).maxWidth;
	}
	int getPreferredWidth(int dataIndex) {
		assert 0 <= dataIndex && dataIndex < infos.size(): dataIndex;
		return getInfo(dataIndex).preferredWidth;
	}
	public void setPreferredWidth(int dataIndex, int width) {
		assert 0 <= dataIndex && dataIndex < infos.size(): dataIndex;
		getInfo(dataIndex).setPreferredWidth(width);
	}
	
	Object getValueAt(int dataIndex) {
		assert 0 <= dataIndex && dataIndex < infos.size(): dataIndex;
		return getInfo(dataIndex).value;
	}
	public void setValueAt(int dataIndex, Object value) {
		assert 0 <= dataIndex && dataIndex < infos.size(): dataIndex;
		getInfo(dataIndex).value = value;
	}
	public int getCount() {
		return infos.size();
	}
	/**
	 * セルの左上の座標(dot)を返す。
	 * @param	viewIndex	表示位置(viewIndex)
	 * @return	セルの左上の座標(dot)
	 */
	int getPoint(int viewIndex) {
		int viewCount = getViewCount();
		assert 0 <= viewIndex && viewIndex <= viewCount: viewIndex;
		Color gridColor = (orientation == JSpread.HORIZONTAL) ? 
				spread.properties.getColor(JSpread.HORIZONTAL_GRID_COLOR) :
				spread.properties.getColor(JSpread.VERTICAL_GRID_COLOR);
		int space = (gridColor != null ? 1 : 0);
		int pos = 0;
		for (int i=0; i<viewIndex; i++) {
			int index = getDataIndex(i);
			pos += getWidth(index) + space;
		}
		return pos;
	}

	/**
	 * セルの右下の座標(dot)を返す。startセルからの相対座標
	 *  @param	viewIndex	表示位置(viewIndex)
	 *  @return	セルの右下の座標(dot)
	 */
	int getTailPoint(int viewIndex) {
		int index = getDataIndex(viewIndex);
		return getPoint(viewIndex) + getWidth(index);
	}
	/**
	 * 指定した座標が、どのセルに位置するかを返す。
	 * @param x
	 * @return	表示index. セルの範囲外のときには、(-1)を返す。
	 */
	int getViewPositionAtPoint(int x) {
		if (x < 0) return -1;
		int p = 0;
		Color gridColor = (orientation == JSpread.HORIZONTAL) ? 
				spread.properties.getColor(JSpread.HORIZONTAL_GRID_COLOR) :
				spread.properties.getColor(JSpread.VERTICAL_GRID_COLOR);
		int space = (gridColor != null ? 1 : 0);
		int viewCount = getViewCount();
		for (int i=0; i<viewCount; i++) {
			int index = getDataIndex(i);
			p += getWidth(index) + space;
			if (x < p) {
				return i;
			}
		}
		return -1;
	}
	int getTotalWidth() {
		return getPoint(getViewCount());
	}
	/**
	 * 表示位置(viewIndex)からデータ位置(dataIndex)を割り出すための対応表を作る.
	 */
	void makeDataIndexList() {
		dataIndexList = new int[getViewCount()];
		for (int i=0; i<infos.size(); i++) {
			if (getInfo(i).viewPosition != -1) { 
				dataIndexList[getInfo(i).viewPosition] = i;
			}
		}
	}
	/** 表示する行/列の数を返す. */
	public int getViewCount() {
		int viewCount = 0;
		for (int i=0; i<infos.size(); i++) {
			if (getInfo(i).viewPosition != -1) viewCount++;
		}
		return viewCount;
	}
	/**
	 * 表示位置(viewIndex)からデータのインデックスを求める.
	 * @param	viewIndex	表示位置(0..)
	 * @return	データのインデックス
	 */
	int getDataIndex(int viewIndex) {
		assert 0 <= viewIndex && viewIndex < dataIndexList.length: viewIndex;
		return dataIndexList[viewIndex];
	}
	public int getAutoResizeMode() {
		return autoResizeMode;
	}
	public void setAutoResizeMode(int mode) {
		assert 	mode == AUTO_RESIZE_OFF ||
				mode == AUTO_RESIZE_NEXT_COLUMN ||
				mode == AUTO_RESIZE_LAST_COLUMN:
				mode;
		this.autoResizeMode = mode;
	}
	
	/** ヘッダ情報. */
	static class HeaderInfo {
		/** ヘッダの値. null可 */
		Object value;
		/** 表示幅(dot). minWidth <= width <= maxWidth */
		int width;
		/** 最小幅(dot).0以上. デフォルトは15. */
		int minWidth;
		/** 最大幅(dot) 0以上 デフォルトはInteger.MAX_VALUE*/
		int maxWidth;
		/** 最適な幅(dot).0以上. 常に(minWidht <= preferredWidth <= maxWidth)が成り立つ */
		int preferredWidth;
		/** 表示位置(viewIndex). -1のときは表示しない */
		int viewPosition;
		/**
		 * ヘッダ情報を作成します。
		 * @param value		ヘッダの値(null可)
		 * @param width		ヘッダの表示幅(dot)(0以上)
		 * @param viewPosition	ヘッダの表示位置(viewIndex). -1のときは表示しない
		 */
		HeaderInfo(Object value, int width, int viewPosition) {
			this.value = value;
			this.minWidth = 15;
			this.maxWidth = Integer.MAX_VALUE;
			this.width = Math.max(minWidth, Math.min(width, maxWidth));
			this.preferredWidth = width;
			this.viewPosition = viewPosition;
		}
		void setWidth(int newWidth) {
//			this.width = Math.max(minWidth, Math.min(newWidth, maxWidth));
			width = newWidth;
		}
		void setPreferredWidth(int newWidth) {
			//this.preferredWidth = Math.max(minWidth, Math.min(newWidth, maxWidth));
			this.preferredWidth = newWidth;
		}
		void setMinWidth(int newWidth) {
			assert newWidth <= maxWidth: newWidth;
//			this.minWidth = Math.max(0, newWidth);
//			if (width < minWidth) width = minWidth;
//			if (preferredWidth < minWidth) preferredWidth = minWidth;
			this.minWidth = newWidth;

		}
		void setMaxWidth(int newWidth) {
			assert minWidth <= newWidth:  newWidth;
//			this.maxWidth = newWidth;
//			if (maxWidth < width) width = maxWidth;
//			if (maxWidth < preferredWidth) preferredWidth = maxWidth;
			this.maxWidth = newWidth;
		}
	}
}
