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


import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.util.ArrayList;
import java.util.Hashtable;

import javax.swing.JFrame;

import jp.ac.kyutech.mns.ist.FocusFrame;
import jp.ac.kyutech.mns.ist.FocusRectangle;
import jp.ac.kyutech.mns.ist.LayoutContent;
import jp.ac.kyutech.mns.ist.LayoutTarget;
import jp.ac.kyutech.mns.ist.Note;
import jp.ac.kyutech.mns.ist.SSHandler;
import jp.ac.kyutech.mns.ist.ShortStroke;
import jp.ac.kyutech.mns.ist.SimpleATN;
import jp.ac.kyutech.mns.ist.ConfigWindow;
import jp.ac.kyutech.mns.ist.util.BUtil;
import jp.ac.kyutech.mns.ist.util.FileReadWriter;
import jp.ac.kyutech.mns.ist.util.MyAction;
import edu.umd.cs.piccolo.PLayer;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.activities.PActivity;
import edu.umd.cs.piccolo.activities.PTransformActivity;
import edu.umd.cs.piccolo.event.PBasicInputEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.nodes.PText;
import edu.umd.cs.piccolo.util.PBounds;
import edu.umd.cs.piccolo.util.PDimension;

/**
 * シートマップ
 * 
 * 枠に筆記があれば、その筆記のペンIDを拡大する
 * 複数の筆記があれば、どちらかを選択し、選択しなかった筆記を消す。または、ジャンプ時に切り替える。
 * 
 * または、ペン故障・交換に対応し，新ペンの筆記を旧パネルに転送マージする
 * マージはいつでもできる。基本的に、番号が小さい方にマージする。
 * 
 * １つの席に、複数のペンID候補がありうる。選択肢としては、マージ、リジェクト、移動。
 * 複数の席に記入したペンがあるとき。後で書いた方のみを有効とする。（前の席のペンID候補、筆記は削除する）
 * 
 * 座席表マップは１つの見方にすぎない。操作者が位置を認識できれば、実際の配置と異なっていてもかまわない。
 * 
 * 筆記がはみ出したとき、重心がどの領域にはいっているかで判断する。
 * 
 * 特殊なビューに筆記を転送する。このRecognizerではなにもしない。
 * 
 * 筆記が入って，マッピングデータが無い＞登録する（stu=>xy, xy=>stu）
 * xy=>stuが複数の場合，優先順を設定できる，またはマージする
 * stu=>xyが存在する場合，前の筆記を消して，上書きする（消すために，stu->ssマップを持っておく）
 * 
 * @author miuramo
 */
public class SeatmapController extends MyPCanvas implements SSHandler, Runnable {
	private static final long serialVersionUID = 5015073847828263031L;

	PLayer buttonLayer;
	PLayer txtNameLayer;
	Rectangle2D regionpb;
	int cols, rows;
	MatrixHolder<LayerSeatmapButton> buttonmat;
	Hashtable<Note, Point> stu2pos;
	MatrixHolder<ArrayList<Note>> pos2stu;
	Hashtable<Note, ArrayList<ShortStroke>> stu2ss;

	double matrixwidth,matrixheight;
	SimpleATN satn;
//	Sheet sheet;
	public String info;
	ArrayList<PText> txtNamePTs;
	ArrayList<ShortStroke> ssDrawings;

	JFrame frame;
	boolean isInit = true;//初回の起動なら，SeatMapツールバートグルボタンをONにする

	private InsideToolbar_SeatMap sheetVisibleToolbuttons;

	public void clean(){
		getLayer().removeAllChildren();
		ssDrawings.clear();
		stu2pos.clear();
		pos2stu.removeAll();
		stu2ss.clear();
		for(LayerSeatmapButton lsb: buttonmat.values()){
			lsb.clean();
		}
	}
	private void preinit(){
		buttonLayer = new PLayer();
		getCamera().addLayer(buttonLayer);

		txtNameLayer = new PLayer();
		getCamera().addLayer(txtNameLayer);
		txtNamePTs = new ArrayList<PText>();
		ssDrawings = new ArrayList<ShortStroke>();

		buttonmat = new MatrixHolder<LayerSeatmapButton>();
		stu2pos = new Hashtable<Note, Point>();
		pos2stu = new MatrixHolder<ArrayList<Note>>();
		stu2ss = new Hashtable<Note,ArrayList<ShortStroke>>();
	}
	private void postinit(){
		//筆記ハンドラの設定！！
		Note.addSSHandler(this);

		double dx = matrixwidth = regionpb.getWidth() / cols;
		double dy = matrixheight = regionpb.getHeight() / rows;
		for(int j = 0 ; j < rows ; j++ ){
			for (int i = 0; i < cols; i++) {
				LayerSeatmapButton lb = new LayerSeatmapButton((regionpb.getX()+ dx * i),(regionpb.getY() + dy * j), dx,dy, getLayer(), this);
				lb.setXY(i,j);
				//				lb.addClickActionListener(this);
				buttonLayer.addChild(lb);
				lb.resetCamByCurrentLocation();
				buttonmat.put(i,j,lb);

			}
		}

		removeInputEventListener(getZoomEventHandler());

		PBasicInputEventHandler handler = new PBasicInputEventHandler() {
			public void mouseEntered(PInputEvent aEvent){			}
			public void mouseExited(PInputEvent aEvent){			}
			public void mousePressed(PInputEvent aEvent) {			}
			/**
			 * ドラッグしたらつまんで動かす
			 */
			public void mouseDragged(PInputEvent aEvent) {
				PDimension d = aEvent.getDelta();
				getCamera().translateView(d.getWidth(), d.getHeight());
			}
			public void mouseReleased(PInputEvent aEvent) {			}
			public void mouseClicked(PInputEvent aEvent){			}
		};
		getCamera().addInputEventListener(handler);

		sheetVisibleToolbuttons = new InsideToolbar_SeatMap(this);
		getCamera().addChild(sheetVisibleToolbuttons);


		//initialize
		frame = new JFrame("Pen Associated Seatmap");
		frame.getContentPane().add(this);
		int seatw = (int)(regionpb.getWidth());
		int seath = (int)(regionpb.getHeight());
		int seatx = satn.getLocation().x+satn.getWidth();
		int seaty = satn.getLocation().y;
		Dimension disp = Toolkit.getDefaultToolkit().getScreenSize();
		if (seatx+seatw > disp.width) {
			seatw = disp.width-seatx;
		}
		frame.setSize(seatw,seath);
		frame.setLocation(seatx,seaty);
		frame.setVisible(true);		
		
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		Note.addSSHandler(this);
		
		//キー入力(ESCキーで画面全体表示)
		addKeyListener(new KeyListener() {
			@Override
			public void keyTyped(KeyEvent e) {		}
			@Override
			public void keyReleased(KeyEvent e) {	}			
			@Override
			public void keyPressed(KeyEvent e) {
				System.out.println("keyCode=" + e.getKeyCode() + " keyChar ["+ e.getKeyChar() + "]");
				if (e.getKeyCode() == 27) {// ESC
					home();
					satn.upview(-1);
					for(FocusFrame ff: FocusFrame.frames){
						if (!ff.isFocusReferenceMode) ff.upview(-1);
					}
				}
				if (37 <= e.getKeyCode() && e.getKeyCode() <= 40){
					for(FocusFrame ff: FocusFrame.frames){
						ff.processCursorKey(e.getKeyCode());
					}
				}
			}
		});
		requestFocus();


		home();//zoom home
		isInit = true;
		layoutLater();
	}

	public SeatmapController(String rs, SimpleATN satn){
		this.satn = satn;
//		rs = "seatmap 6,5x4,120,340,560,780";
		//example of rs: seatmap 6,6x7,100.0,100.0,400.0,300.0 
		String[] p = rs.split(" ");
		for(String pp:p){
			System.out.println(pp);
		}
		int sheetnum = Integer.parseInt(p[1]);
		int col = Integer.parseInt(p[2]);
		int row = Integer.parseInt(p[3]);
		regionpb = new PBounds(Double.parseDouble(p[4]),Double.parseDouble(p[5]),Double.parseDouble(p[6]),Double.parseDouble(p[7]));
		info = "on Sheet "+sheetnum;
		preinit();
		cols = col;
		rows = row;
		postinit();
	}
	public SeatmapController(Rectangle2D rp, SimpleATN satn, int sheetnum){
		this.satn = satn;
		regionpb = rp;
		info = "on Sheet "+sheetnum;
		preinit();
		cols = ConfigWindow.configWin.seatmap.x;//TODO:
		rows = ConfigWindow.configWin.seatmap.y;//TODO:
		postinit();
		//書き出し
		ArrayList<String> setup = FileReadWriter.getLinesList(satn.pimagePath+File.separator+"setup.txt", true);
		setup.add("seatmap "+sheetnum+" "+cols+" "+rows+" "+rp.getX()+" "+rp.getY()+" "+rp.getWidth()+" "+rp.getHeight());
		FileReadWriter.putLines(satn.pimagePath+File.separator+"setup.txt", setup);
	}
	public void layoutLater(){
		Thread t = new Thread(this);
		t.start(); //時間差layout
	}
	public void run(){
		if (isInit){
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			MyAction seatmapA;
			if (satn.menutoolbar != null){
				seatmapA = satn.menutoolbar.getAction("SheetMap");
				if (seatmapA != null && !seatmapA.isSelected()) {
					seatmapA.setSelected(true);
				}
			}
			isInit = false;
			ConfigWindow.configWin.seatmap.smaps.add(this);
			ConfigWindow.configWin.seatmap.active_smap = this;
		}
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//		layout(); 下でかわりにやってもらう
		satn.upview(-1);
		for(FocusFrame ff: FocusFrame.frames){
			ff.sortByToolbarButtonStatus();
		}
		
	}
	public void home(){
		PBounds pb = buttonLayer.getFullBounds();
		getCamera().animateViewToCenterBounds(pb, true, 100);
	}
	public void layout(){}
	public void layout(LayoutTarget lt){
		lt.layoutBySeatmap();
	}
	public Hashtable<Note, Point> getStu2pos(){
		return stu2pos;
	}
	public Point getPointByLayoutContent(LayoutContent l){
		if (l instanceof Note) return stu2pos.get((Note)l);
		else if (l instanceof FocusRectangle){
			FocusRectangle fr = (FocusRectangle) l;
			return stu2pos.get(fr.getNote());
		}
		return null;
	}
	public void action(String label){
		if (label.equals("open")){
			frame.setVisible(true);
		} else if (label.equals("layout")){
			//			frame.setVisible(false);
			layout(satn);
		} else if (label.equals("active")){
			ConfigWindow.configWin.seatmap.active_smap = this;
		}
	}

	@Override
	public void process(ShortStroke ss) {
		addStroke(ss);
	}

	@Override
	public void processPrevious(ShortStroke ss) {
		addStroke(ss);	
	}

	/**
	 * ケース１：該当stuに枠が無い
	 * ケース２：該当stuに枠あり，同じ枠なので，そのまま
	 * ケース3：該当stuに枠あり，別枠なので，移動（前の枠からはずし，新しい枠に登録）
	 * 
	 * 枠ケース１：枠にstuがない＞プライマリstuに登録
	 * 枠ケース２：枠にstuがある＞セカンダリstuに登録
	 * 枠ケース３：
	 *  * 筆記が入って，マッピングデータが無い＞登録する（stu=>xy, xy=>stu）
	 * xy=>stu(pos2stu)が複数の場合，優先順を設定できる，またはマージする
	 * stu=>xy(stu2pos)が存在する場合，前の筆記を消して，上書きする（消すために，stu->ssマップを持っておく）
	 * @param origss
	 * @param stu
	 */
	public void addStroke(ShortStroke origss){
		if (origss.penid == 0) return;

		ShortStroke ss = origss.clone();
		ss.rebuildStroke();
		Note n = satn.getNote(ss.penid);
		addStroke(ss,n);
	}
	public void addStroke(ShortStroke ss, Note stu){

		//領域に，筆記の中心がふくまれているとき「のみ」，筆記を追加（ボタンではなく，背景に）
		if (regionpb.contains(ss.getBounds().getCenter2D())){
			getLayer().addChild(ss);
			//		getLayer().repaint();
			ssDrawings.add(ss);
		}

		//筆記位置を解析
		//(1)筆記中心を計算
		PBounds a = ss.getBoundsReference();
		Point2D p2 = a.getCenter2D();

		double zeropx = regionpb.getX();
		double zeropy = regionpb.getY();

		// 筆記がどこのマスに書かれたものかを判定
		double tempx = (p2.getX() - zeropx) / matrixwidth;
		double tempy = (p2.getY() - zeropy) / matrixheight;
		int locx = (int) tempx;
		int locy = (int) tempy;


		int teacher = ConfigWindow.configWin.draw.tpid;
		if (stu.penid == teacher){
			if ((locx < 0 || ConfigWindow.configWin.seatmap.x <= locx) ||
					(locy < 0 || ConfigWindow.configWin.seatmap.y <= locy)){
				//				System.out.println("Draw on delegate");
				Note n = satn.getNote(ConfigWindow.configWin.draw.target4teacher);
				ShortStroke copyss = ss.clone();
				copyss.penid = ConfigWindow.configWin.draw.target4teacher;
				copyss.color = 0xff0000;
				n.addStroke(copyss,true);
			} else {
				//タップ位置の生徒IDを，今後の書き込みIDにする
				ArrayList<Note> targets = pos2stu.get(locx,locy);
				if (targets != null && targets.size()>0){
					Note target = targets.get(0);
					ConfigWindow.configWin.draw.target4teacher = target.penid;
					//					System.out.println("teachertarget set to "+ target.penid);
					return;
				}
			}
		} else {
			if (locx < 0 || ConfigWindow.configWin.seatmap.x <= locx) return;
			if (locy < 0 || ConfigWindow.configWin.seatmap.y <= locy) return;
		}

		//登録
		Point p = stu2pos.get(stu);
		if (p == null){
			System.out.println(stu.penid+" "+locx+" "+locy+" new");
			//まだ座席登録がなかった
			p = new Point(locx,locy);
			stu2pos.put(stu, p);
			addPos2Stu(locx, locy, stu);
			addStu2SS(stu, ss);
			LayerSeatmapButton lsb = buttonmat.get(locx, locy);
			if (lsb != null) lsb.addJump(stu);
		} else {
			if (p.x == locx && p.y == locy){
				System.out.println(stu.penid+" "+locx+" "+locy+" same");
				//登録されている場所と同じなので，なにもしない
				addStu2SS(stu, ss);
			} else {
				//席を移動した
				System.out.println(stu.penid+" "+locx+" "+locy+" move");

				stu2pos.put(stu, new Point(locx,locy));
				removePos2Stu(p.x,p.y,stu);//前の席のマトリックスからstuを抜く
				LayerSeatmapButton lsb = buttonmat.get(p.x, p.y);
				if (lsb != null){
					lsb.removeJump(stu);
					lsb.addLeave(stu);
				}

				addPos2Stu(locx,locy,stu);//新しい席マトリックスにstu登録:複数のときの処理はメソッドに任せる
				for(ShortStroke sss : stu2ss.get(stu)){
					sss.removeFromParent();
					ssDrawings.remove(sss);
				}
				stu2ss.get(stu).clear();//クリア
				repaint();
				addStu2SS(stu, ss);
				LayerSeatmapButton lsb2 = buttonmat.get(locx, locy);
				if (lsb2 != null) lsb2.addJump(stu);
			}
		}
	}
	public void addPos2Stu(int x, int y, Note student){
		ArrayList<Note> as = pos2stu.get(x, y);
		if (as == null){
			as = new ArrayList<Note>();
			as.add(student);
			pos2stu.put(x, y, as);
		} else {
			as.add(student);
		}
	}
	public void removePos2Stu(int x, int y, Note student){
		ArrayList<Note> as = pos2stu.get(x, y);
		if (as == null){
			System.out.println("Stuを抜くにも，登録がなかった");
		} else {
			as.remove(student);
		}
	}
	public void addStu2SS(Note student, ShortStroke sss){
		ArrayList<ShortStroke> ass = stu2ss.get(student);
		if (ass == null){
			ass = new ArrayList<ShortStroke>();
			stu2ss.put(student, ass);
		}
		ass.add(sss);
	}

	public void hilit(Object attribute, Color pencolor) {
		Note stu = (Note)attribute;
		ArrayList<ShortStroke> ass = stu2ss.get(stu);
		for(ShortStroke ss: ass){
			ss.setStrokePaint(pencolor);
		}
	}

	public void setWorldRotation(int d) {

		ArrayList<PNode> nodes = new ArrayList<PNode>();
		for(int j = 0 ; j < rows ; j++ ){
			for (int i = 0; i < cols; i++) {
				LayerSeatmapButton lsb = buttonmat.get(i,j);
				nodes.add(lsb);
			}
		}

		PBounds retPB = new PBounds();
		ArrayList<PActivity> palist = BUtil.layoutMatrixPointsInDirection(nodes,d, cols, rows, regionpb.getCenterX(), regionpb.getCenterY(), 1000, retPB);

		long mtime = System.currentTimeMillis();
		for (PActivity pact : palist){
			pact.setStartTime(mtime);
		}

		retPB = BUtil.zoomBounds(retPB, 1.00);
		PTransformActivity zoomHomeAct = getCamera().animateViewToCenterBounds(retPB, true, 100);
		zoomHomeAct.setStartTime(mtime);
	}
}
