/*
 * 作成日: 2007/02/11
 */
package net.sourceforge.swingx.jspread;

import java.awt.Cursor;
import java.awt.event.MouseEvent;
import java.util.ArrayList;

import net.sourceforge.swingx.Logger;
import net.sourceforge.swingx.jspread.JSpreadHeader.HeaderInfo;

/**
 * ヘッダの幅をマウスによって変更する.
 * これは、JSpreadHeaderListenerから委譲されて呼ばれる。
 * @author Asan
 */
public class JSpreadHeaderResizeListener {
	private static Logger logger = Logger.getLogger(JSpreadHeaderResizeListener.class);

	final JSpreadHeaderPane pane;
	final JSpread spread;
	int resizingViewIndex;
	
	JSpreadHeaderResizeListener(JSpread spread, JSpreadHeaderPane pane) {
		assert spread != null;
		assert pane != null;		
		this.spread = spread;
		this.pane = pane;
	}
	
	void mousePressed(MouseEvent e) {
		switch (pane.orientation) {
		case JSpread.COLUMN:
			resizingViewIndex = getResizePosition(spread.sheet.colHeader, e.getX());
			break;
		case JSpread.ROW:
			resizingViewIndex = getResizePosition(spread.sheet.rowHeader, e.getY());
			break;
		default:
			assert false: pane.orientation;
		}
	}
	/** コンポーネント上でマウスのボタンを押してドラッグすると呼び出されます。 */
	public void mouseDragged(MouseEvent e) {
		logger.debug(e);
		if (e.isConsumed()) return;
//		JSpreadSheet sheet = spread.getSheet();

//		int new_w;
//		switch (pane.orientation) {
//		case JSpread.ROW:
//			int startRow = 0;
//			new_w = e.getY() - 
//				(sheet.rowHeader.getPoint(resizingViewIndex) - sheet.rowHeader.getPoint(startRow));
//			if (new_w < 0) break;
//			changeWidth(sheet.rowHeader, resizingViewIndex, new_w);
//			spread.再配置();
//			pane.repaint();
//			break;
//		case JSpread.COLUMN:
//			int startCol = 0;
//			new_w = e.getX() - 
//				(sheet.colHeader.getPoint(resizingViewIndex) - sheet.colHeader.getPoint(startCol));
//			if (new_w < 0) break;
//			changeWidth(sheet.colHeader, resizingViewIndex, new_w);
//			spread.再配置();
//			pane.repaint();
//			break;
//		default:	
//			assert false;
//		}
	}
	// TODO:AUTO_RESIZE_NEXT_COLUMN, ...などに対応すること
	private void changeWidth(JSpreadHeader header, int pos, int new_width) {
		int index;
		switch (header.autoResizeMode) {
		case JSpread.AUTO_RESIZE_OFF:
			index = header.getDataIndex(pos);
			header.getInfo(index).setWidth(new_width);
			break;
		case JSpread.AUTO_RESIZE_NEXT_COLUMN:
			if (pos == header.getViewCount()-1) break;
			index = header.getDataIndex(pos);
			header.getInfo(index).setWidth(new_width);
			break;
		case JSpread.AUTO_RESIZE_LAST_COLUMN:
			break;
		default:
			assert false: header.autoResizeMode;
		}
	}
	
	public void mouseReleased(MouseEvent e) {
		if (e.isConsumed()) return;
		JSpreadSheet sheet = spread.getSheet();

		// 新しい幅を求める
		int new_w = (pane.orientation == JSpread.ROW) ?
					e.getY() - sheet.rowHeader.getPoint(resizingViewIndex):
					e.getX() - sheet.colHeader.getPoint(resizingViewIndex);

		if (new_w < 0) return;
		System.out.println("new_w="+new_w+" resizingViewIndex="+resizingViewIndex);
		// もし、リサイズした列/行が選択していた場合、他の選択した列/行も同じ幅にする。
		if (spread.getHeaderSelection(pane.orientation, resizingViewIndex)) {
			int[] viewIndexList = spread.getHeaderSelections(pane.orientation);
			int[] newWidthList = new int[viewIndexList.length];
			for (int i=0; i<viewIndexList.length; i++) {
				newWidthList[i] = new_w;
			}
			requestResize(viewIndexList, newWidthList);
		}
		else {
			requestResize(new int[]{resizingViewIndex}, new int[]{new_w});
		}
		spread.再配置();
		e.consume();
	}

    /** コンポーネント上でマウスボタンをクリック (押してから離す) したときに呼び出されます。*/ 
	public void mouseClicked(MouseEvent e) {
		if (e.isConsumed()) return;
		// 左ボタンでダブルクリックしたとき、ヘッダの幅を最適な幅にする。
		JSpreadSheet sheet = spread.getSheet();
		if (e.getClickCount() == 2 && e.getButton()==MouseEvent.BUTTON1) {
			logger.debug(e);
			
			switch (pane.orientation) {
			case JSpread.ROW:
				if (! sheet.allowAutoRowHeaderResize) return;
				int pos = getResizePosition(sheet.rowHeader, e.getY());
				if (pos == -1) break;	// 境界でない場合は何もしない
				int rowDataIndex = sheet.rowHeader.getDataIndex(pos);
				// 表示しているその列のすべてのセルについて、最適なサイズを求める
				int width = spread.headerController.getPreferredWidth(pane.orientation, rowDataIndex);
				// その行が選択されていたら、選択しているすべての行の幅を変える。
				if (sheet.getHeaderSelection(pane.orientation, pos)) {
					// 選択しているすべての行の幅を変える。
					int[] viewIndexList = spread.getHeaderSelections(pane.orientation);
					int[] newWidthList = new int[viewIndexList.length];
					for (int i=0; i<newWidthList.length; i++) {
						newWidthList[i] = width;
					}
					requestResize(viewIndexList, newWidthList);
				}
				else {
					// その行が選択されていなかったら、その行だけを変える。
					requestResize(new int[] {pos}, new int[] {width});
				}
				spread.再配置();
				e.consume();
				break;
			case JSpread.COLUMN:
				if (! sheet.allowAutoColHeaderResize) return;
				pos = getResizePosition(sheet.colHeader, e.getX());
				if (pos == -1) break;	// 境界でない場合は何もしない
				int colDataIndex = sheet.colHeader.getDataIndex(pos);
				// 表示しているその列のすべてのセルについて、最適なサイズを求める
				width = spread.headerController.getPreferredWidth(pane.orientation, colDataIndex);
				// その行が選択されていたら、選択しているすべての行の幅を変える。
				if (sheet.getHeaderSelection(pane.orientation, pos)) {
					// 選択しているすべての行の幅を変える。
					int[] viewIndexList = spread.getHeaderSelections(pane.orientation);
					int[] newWidthList = new int[viewIndexList.length];
					for (int i=0; i<newWidthList.length; i++) {
						newWidthList[i] = width;
					}
					requestResize(viewIndexList, newWidthList);
				}
				else {
					// その行が選択されていなかったら、その行だけを変える。
					requestResize(new int[] {pos}, new int[] {width});
				}
				spread.再配置();
				e.consume();
				break;
			case JSpread.CORNER:
				break;
			default:	assert false;
			}
		}
	}
	/**
	 * 指定したviewIndexの幅を変更する。
	 * 必ずしも要求したとおりの幅になるとは限らない。
	 *
	 * @param viewIndexList
	 * @param newWidthList
	 * @return
	 */
	public boolean requestResize(int[] viewIndexList, int[] newWidthList) {
		assert viewIndexList != null;
		assert newWidthList != null;
		assert viewIndexList.length == newWidthList.length:
			"要素数が違う "+viewIndexList.length + " " + newWidthList.length;
		assert viewIndexList.length > 0: viewIndexList.length;
		
		JSpreadHeader header = (pane.orientation == JSpread.ROW) ? 
								spread.sheet.rowHeader:
								spread.sheet.colHeader;

		// 要求した幅をminWidth,maxWidthに収める。
		int[] newWidthList2 = new int[newWidthList.length];
		HeaderInfo[] info_list = new HeaderInfo[newWidthList.length];
		int newWidthList2total = 0;
		for (int i=0; i<newWidthList.length; i++) {
			int dataIndex = header.getDataIndex(viewIndexList[i]);
			info_list[i] = header.getInfo(dataIndex);
			newWidthList2[i] = minmax(newWidthList[i], 
					info_list[i].minWidth, info_list[i].maxWidth);
			newWidthList2total += newWidthList2[i];
		}
		
		// 自動リサイズモードがOFFならば、上下限チェックした要求どおりのサイズになる。
		if (header.autoResizeMode == JSpread.AUTO_RESIZE_OFF) {
			for (int i=0; i<newWidthList.length; i++) {
				info_list[i].width = newWidthList2[i];
			}	
			// TODO:ヘッダペインの幅を変えなくてもよいか？
			return true;
		}
		
		// 調整対象のカラムを求める
		ArrayList 調整IndexList = new ArrayList();
		switch (header.autoResizeMode) {
		case JSpread.AUTO_RESIZE_NEXT_COLUMN:	// 次のカラム
		{	// 複数のカラムのうち、最も末尾のカラムの次のカラム
			int maxIndex = max(viewIndexList);
			if (maxIndex+1 < header.dataIndexList.length) {
				調整IndexList.add(new Integer(maxIndex+1));
			}
			break;
		}
//		case JSpread.AUTO_RESIZE_SUBSEQUENT_COLUMNS:
//		{	// 複数のカラムのうち、最も末尾のカラム以降
//			int maxIndex = max(viewIndexList);
//			for (int i=maxIndex+1; i<header.dataIndexList.length; i++) {
//				調整IndexList.add(new Integer(i));
//			}
//			break;
//		}		
		case JSpread.AUTO_RESIZE_LAST_COLUMN:
		{	// 一番最後のカラム
			int maxIndex = max(viewIndexList);
			if (maxIndex+1 < header.dataIndexList.length) {
				調整IndexList.add(new Integer(header.dataIndexList.length-1));
			}
			break;
		}		
//		case JSpread.AUTO_RESIZE_ALL_COLUMNS:
//			for (int i=0; i<header.dataIndexList.length; i++) {
//				if (! contains(viewIndexList, i)) {
//					調整IndexList.add(new Integer(i));
//				}
//			}
//			break;
		default:
			assert false: header.autoResizeMode;
		}
		
		// 調整することができるカラムがなければ、要求どおりの幅に設定することができない。
		if (調整IndexList.size() == 0) return false;
		
		// 全体のサイズ
		int totalWidth = pane.orientation == JSpread.COLUMN ?
					pane.getWidth(): pane.getHeight();
		//　増分を求める
		int delta = 0;
		for (int i=0; i<info_list.length; i++) {
			delta += (newWidthList2[i] - info_list[i].width);
		}
		System.out.println("全体のサイズ="+totalWidth+" 増分="+delta);
		long max_width = 0L;
		long min_width = 0L;
		long cur_width = 0L;
		for (int i=0; i<調整IndexList.size(); i++) {
			int dataIndex = ((Integer) 調整IndexList.get(0)).intValue();
			System.out.println(調整IndexList.get(i));
			max_width += header.getInfo(dataIndex).maxWidth;
			min_width += header.getInfo(dataIndex).minWidth;
			cur_width += header.getInfo(dataIndex).width;
		}
		System.out.println("受け入れ可能な幅="+min_width+" "+max_width+" "+cur_width+" delta="+delta);
		// 受け入れ可能な範囲に入っていない。
		if (min_width > cur_width-delta || cur_width-delta > max_width) return false;
		// TODO:受け入れ可能な範囲に入っているので、それを按分する。
		System.out.println("受け入れ可能");
		if (調整IndexList.size() == 1) {
			for (int i=0; i<newWidthList.length; i++) {
				info_list[i].width = newWidthList2[i];
			}	
			int dataIndex = ((Integer) 調整IndexList.get(0)).intValue();
			header.getInfo(dataIndex).width -= delta;
			return true;
		}
		// TODO:要素が複数の時には難しい。
		
		
		return false;
	}
	private int minmax(int value, int min, int max) {
		if (value < min) return min;
		if (value > max) return max;
		return value;
	}
	private int max(int[] values) {
		int maxValue = values[0];
		for (int i=0; i<values.length; i++) {
			if (values[i] > maxValue) {
				maxValue = values[i];
			}
		}
		return maxValue;
	}
//	private boolean contains(int[] values, int value) {
//		for (int i=0; i<values.length; i++) {
//			if (values[i] == value) return true;
//		}
//		return false;
//	}
	int getResizePosition(JSpreadHeader header, int x) {
		int pos1 = header.getViewPositionAtPoint(x-5);
		int pos2 = header.getViewPositionAtPoint(x+5);
		//logger.debug("pos1="+pos1+" pos2="+pos2);
		if (pos1 != pos2 && pos1 != -1) {
			return pos1; 
		}
		return -1;
	}
	boolean isSensable(int x, int y) {
		switch (pane.orientation) {
		case JSpread.COLUMN:
			if (! spread.sheet.allowAutoColHeaderResize) return false;
			return getResizePosition(spread.sheet.colHeader, x) != -1;
		case JSpread.ROW:
			if (! spread.sheet.allowAutoRowHeaderResize) return false;
			return getResizePosition(spread.sheet.rowHeader, y) != -1;
		default:
			return false;
		}
	}
	void mouseMoved(MouseEvent e) {
		switch (pane.orientation) {
		case JSpread.COLUMN:
			pane.setCursor(spread.properties.getCursor(JSpread.HEADER_RESIZE_H_CURSOR));
			break;
		case JSpread.ROW:
			pane.setCursor(spread.properties.getCursor(JSpread.HEADER_RESIZE_V_CURSOR));
			break;
		default:
			assert false: pane.orientation;
		}
	}

	Cursor getCursor() {
		switch (pane.orientation) {
		case JSpread.COLUMN:
			return spread.properties.getCursor(JSpread.HEADER_RESIZE_H_CURSOR);
		case JSpread.ROW:
			return spread.properties.getCursor(JSpread.HEADER_RESIZE_V_CURSOR);
		default:
			assert false: pane.orientation;
		}
		return null;
	}
}
