package jp.ac.kyutech.mns.ist.sheetmap;

import java.awt.Point;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.TreeMap;

/**
 * マス目に文字列などのデータを結びつけるクラス
 * 
 * ここでは，特に(int,int) -> String を意識して，putメソッドを作成している
 * 
 */
public class MatrixHolder<V> {
	Hashtable<String, V> datahash;
	Hashtable<V,Point> data2pos;
	TreeMap<Integer,ArrayList<V>> rows, cols;
	int xmin = 9,xmax = -1, ymin = 9, ymax = -1;

	public MatrixHolder() {
		datahash = new Hashtable<String, V>();
		data2pos = new Hashtable<V, Point>();
		rows = new TreeMap<Integer, ArrayList<V>>();
		cols = new TreeMap<Integer, ArrayList<V>>();
	}

	public void put(int x, int y, V val) {
		String key = String.valueOf(x) + ":" + String.valueOf(y);
		datahash.put(key, val);
		data2pos.put(val, new Point(x,y));
		ArrayList<V> cv = cols.get(x);
		if (cv == null){
			cv = new ArrayList<V>();
			cols.put(x, cv);
		}
		cv.add(val);
		if (x > xmax) xmax = x;
		if (x < xmin) xmin = x;
		
		ArrayList<V> dv = rows.get(y);
		if (dv == null){
			dv = new ArrayList<V>();
			rows.put(y, dv);
		}
		dv.add(val);
		if (y > ymax) ymax = y;
		if (y < ymin) ymin = y;
	}
	//オブジェクトから位置を探す
	public Point getPos(V val){
		return data2pos.get(val);
	}
	//位置からオブジェクトを探す
	public V get(int x, int y) {
		String key = String.valueOf(x) + ":" + String.valueOf(y);
		return datahash.get(key);
	}
	//隣接オブジェクトを探す
	public V getUDLR(int keyCode, V current){
		Point pos = getPos(current);
//		System.out.println(pos);
		if (pos == null) return null;
		if (keyCode == KeyEvent.VK_UP) {
			//上になければ，null
			if (pos.y <= ymin) return null;
			Integer c = rows.floorKey(pos.y-1);
			if (c == null) return null;
			ArrayList<V> cv = rows.get(c);
			return findNearest(pos.x, cv, true);
		} else if (keyCode == KeyEvent.VK_DOWN) {
			if (pos.y >= ymax) return null;
			Integer c = rows.ceilingKey(pos.y+1);
			if (c == null) return null;
			ArrayList<V> cv = rows.get(c);
			return findNearest(pos.x, cv, true);
		} else if (keyCode == KeyEvent.VK_LEFT) {
			//列を取得
			V can = findLeft(pos.x, rows.get(pos.y));
			if (can == null){
				can = getPreviousRowTail(current);
			}
			return can;
		} else if (keyCode == KeyEvent.VK_RIGHT) {
			//列を取得
			V can = findRight(pos.x, rows.get(pos.y));
			if (can == null){
				can = getNextRowHead(current);
			}
			return can;
//			if (pos.x >= xmax) return getNextRowHead(current);
//			Integer c = cols.ceilingKey(pos.x+1);
//			if (c == null) return null;
//			ArrayList<V> cv = cols.get(c);
//			return findNearest(pos.y, cv, false);
		}
		return null;
	}
	public V getNextRowHead(V current){
//		System.out.println("nexthead");
		Point pos = getPos(current);
		if (pos == null) return null;
		Integer c = rows.ceilingKey(pos.y+1);
		if (c == null) return null;
		ArrayList<V> cv = rows.get(c);
		return findNearest(0, cv, true);
	}
	public V getPreviousRowTail(V current){
//		System.out.println("prevtail");
		Point pos = getPos(current);
		if (pos == null) return null;
		Integer c = rows.floorKey(pos.y-1);
		if (c == null) return null;
		ArrayList<V> cv = rows.get(c);
		return findNearest(100, cv, true);
	}
	//posよりも左を探す
	public V findLeft(int pos, ArrayList<V> cv){
		V candidate = null;
//		System.out.println("POS="+pos);
		int diff = Integer.MAX_VALUE; //現在位置との差は小さいほうがよい
		for(V c: cv){
			Point ps = getPos(c);
			int p;
			p = ps.x;
			if (p >= pos) continue;
			int d = pos-p;
			if (d < diff) {
				diff = d;  candidate = c;
			}
		}
		return candidate;
	}
	//posよりも右を探す
	public V findRight(int pos, ArrayList<V> cv){
		V candidate = null;
//		System.out.println("POS="+pos);
		int diff = Integer.MAX_VALUE;
		for(V c: cv){
			Point ps = getPos(c);
			int p;
			p = ps.x;
			if (p <= pos) continue;
			int d = p-pos;
			if (d < diff) {
				diff = d;  candidate = c;
			}
		}
		return candidate;
	}
	public V findNearest(int pos, ArrayList<V> cv, boolean isrow){
		V candidate = null;
//		System.out.println("POS="+pos);
		int diff = Integer.MAX_VALUE;
		for(V c: cv){
			Point ps = getPos(c);
			int p;
			if (isrow) p = ps.x; else p = ps.y;
			int d = Math.abs(pos-p);
			if (d < diff) {
				diff = d;  candidate = c;
			}
		}
		return candidate;
	}

	public void removeAll() {
		datahash.clear();
		data2pos.clear();
		rows.clear();
		cols.clear();
		xmin = 9;xmax = -1; ymin = 9; ymax = -1;
	}

	public void remove(int x, int y) {
		String key = String.valueOf(x) + ":" + String.valueOf(y);
		// datahash.put(key,null);
		datahash.remove(key);
	}
	public Collection<V> values(){
		return datahash.values();
	}
}
