package jp.ac.kyutech.mns.ist;


import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeMap;

import javax.swing.JPopupMenu;

import jp.ac.kyutech.mns.ist.plugin.DefaultPlugin;
import jp.ac.kyutech.mns.ist.plugin.WindowManagerPlugin;
import jp.ac.kyutech.mns.ist.util.BUtil;
import jp.ac.kyutech.mns.ist.util.MyAction;
import jp.ac.kyutech.mns.ist.util.WheelRotationHandler;
import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.PCanvas;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.event.PInputEventFilter;
import edu.umd.cs.piccolo.event.PPanEventHandler;
import edu.umd.cs.piccolo.util.PBounds;
import edu.umd.cs.piccolo.util.PDimension;
import edu.umd.cs.piccolox.PFrame;

public class FocusFrame extends PFrame implements Runnable, LayoutTarget {
	private static final long serialVersionUID = 2276191088134378532L;
	transient SimpleATN satn;
	transient PCanvas satncanvas;
	//	Rectangle2D region;
	Rectangle2D sheetb;
	transient Sheet targetsheet;

	ZoomRegionHandler zrh;//Event_onSheetが参照するため
	Event_onFF event_onff;

	//	ArrayList<FocusRectangle> rects;
	transient TreeMap<Integer,FocusRectangle> rects_bysid;
//	transient ArrayList<FocusRectangle> rects_sorted = new ArrayList<FocusRectangle>();
	int layoutcols = 0;
	PBounds tempAfterLayoutBounds = null;
	LayoutAbstract layoutActive = null;
	int sorttype = 1; // 1: bysid, 2: byamount, 3: byseat
	LayoutBySeat layoutseat;
	LayoutByIDHorizontal layoutH;
	LayoutByAmountHorizontal layoutA;
	LayoutByTeinei layoutT;

	//あとでNoteが追加された場合に，適用するために履歴をとっておく
	transient public static ArrayList<FocusFrame> frames = new ArrayList<FocusFrame>();

	ArrayList<DefaultPlugin> plugins;
	InsideToolbar_FocusFrame insideToolbar;
	WindowManagerPlugin windowmanager;

	public PCamera getCamera(){
		return getCanvas().getCamera();
	}
	public static void updateFocusReference(int sid){
		for(FocusFrame ff: frames){
			if (ff.isFocusReferenceMode){
				if (ff.rects_bysid!=null)
					if (ff.rects_bysid.get(sid)!=null)
						ff.rects_bysid.get(sid).focusReference();
			}
		}
	}
	public void applyToNote(Note n, boolean doLayout){
//		Sheet s = n.sheets.get(targetsheet.num);
		Sheet s = n.getSheet(targetsheet.num);
		if (s==null) {
			System.out.println("Trying to apply note, but sheet is null "+n.penid);
			return;
		}
		//		Rectangle2D sheetb = targetsheet.globalToLocal(region);
		//		System.out.println("region1  "+region.toString());
		//		Rectangle2D sheetb = s.globalToLocal(region);//こ，これは破壊的メソッド(なぜかregionが壊れる)だったのか．．． 危険だ〜TODO:
		//		System.out.println("sheetb  "+sheetb.toString());

		//		PAffineTransform at = new PAffineTransform();
		//		s.getLocalToGlobalTransform(at);
		//		System.out.println("at "+at.toString());
		//		s.getGlobalToLocalTransform(at);
		//		Rectangle2D sgb = new Rectangle2D.Double();
		//		at.transform(region, sgb);
		//		layer2.addChild(new FocusRectangle(gdim, s, sgb));
		//		PDimension gdim = new PDimension(region.getWidth(), region.getHeight());
		PDimension gdim = new PDimension(sheetb.getWidth(), sheetb.getHeight());
		//		System.out.println("sgb  "+sgb.toString());

		//		FocusRectangle fr = new FocusRectangle(gdim, s, sgb, getCanvas());
		FocusRectangle fr = new FocusRectangle(gdim, s, sheetb, getCanvas());
		rects_bysid.put(n.penid, fr);
		//		System.out.println("region2  "+region.toString());
		//		FocusSrc fs = new FocusSrc(region, fr, this);
		FocusSrc fs = new FocusSrc(sheetb, fr, this, n);
		s.addChild(fs);
		fr.setFocusSrc(fs);

		getCanvas().getLayer().addChild(fr);

//		if (sorttype==1) sortByPenID();
//		else if (sorttype==2) sortByDrawingAmount();
//		else if (sorttype==3) sortBySeat();
		if (doLayout) {
			relayout();
			zoomToBounds(tempAfterLayoutBounds);
		}
	}

	public void setTitle(String base, Rectangle2D _r){
		StringBuffer sb = new StringBuffer();
		sb.append(base);
		sb.append((int)(_r.getWidth()));
		sb.append(" x ");
		sb.append((int)(_r.getHeight()));
		sb.append(" @ ");
		sb.append((int)(_r.getX()));
		sb.append(" , ");
		sb.append((int)(_r.getY()));
		setTitle(sb.toString());
	}
	//	public static FocusFrame createNew(String title, SimpleATN _satn, Rectangle2D _r, Sheet _ts){
	//		FocusFrame ff = new FocusFrame( title,  _satn,  _r,  _ts);
	//		return ff;
	//	}
	public FocusFrame(String title, SimpleATN _satn, Rectangle2D _r, Sheet _ts, boolean isReference, boolean isFullScreen){
		super(title, false, null);
		//		region = _r;
		targetsheet = _ts;
		satn = _satn;
		satncanvas = satn.canvas;
		rects_bysid = new TreeMap<Integer, FocusRectangle>();
		frames.add(this);
		setFullScreenMode(false);

		sheetb = _r;
		setTitle(title, sheetb);

		for(Note n: SimpleATN.theapp.notes.values()){
			applyToNote(n,false);
		}
		setDefaultCloseOperation(javax.swing.WindowConstants.HIDE_ON_CLOSE);

		//remove default zoom event hander
		getCanvas().removeInputEventListener(getCanvas().getZoomEventHandler());
		getCanvas().removeInputEventListener(getCanvas().getPanEventHandler());

		// Button1 - Pan
		PPanEventHandler panEventHandler = new PPanEventHandler();
		panEventHandler.setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK));
		getCanvas().addInputEventListener(panEventHandler);

		// Button2 Wheel Rotation - Zoom
		WheelRotationHandler wrHandler = new WheelRotationHandler(getCanvas(), getCanvas().getCamera());
		wrHandler.setEventFilter(new PInputEventFilter(InputEvent.BUTTON2_MASK));
		getCanvas().addInputEventListener(wrHandler);

		// Button3 Drag - Zoom Region
		zrh = new ZoomRegionHandler(this.getCanvas());
		getCanvas().addInputEventListener(zrh);
		zrh.setEventFilter(new PInputEventFilter(InputEvent.BUTTON3_MASK));

		getCanvas().getPanEventHandler().setEventFilter(new PInputEventFilter(InputEvent.BUTTON3_MASK));

		//プラグインとして，インスペクタを追加
		plugins = new ArrayList<DefaultPlugin>();
		plugins.add(windowmanager = new WindowManagerPlugin(getCanvas(), this));
		plugins.add(new InspectorPlugin(getCanvas(), this));

		event_onff = new Event_onFF(this);
		getCanvas().getCamera().addInputEventListener(event_onff);

		//キー入力(ESCキーで画面全体表示)
		getCanvas().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
					upview();
				}
				if (e.getKeyCode() == 34) {// PageDown + 59
					PCamera pc = getCanvas().getCamera();
					PBounds pb = pc.getViewBounds();
					pb = BUtil.zoomBounds(pb, 0.9);
					pc.animateViewToCenterBounds(pb, true, 0);
				}
				if (e.getKeyCode() == 33) {// PageUp -
					PCamera pc = getCanvas().getCamera();
					PBounds pb = pc.getViewBounds();
					pb = BUtil.zoomBounds(pb, 1.1);
					pc.animateViewToCenterBounds(pb, true, 0);
				}
				if (37 <= e.getKeyCode() && e.getKeyCode() <= 40){
					processCursorKey(e.getKeyCode());
				}
				if (e.getKeyCode() == KeyEvent.VK_F11){

				}
				//				if (e.getKeyCode() == KeyEvent.VK_Z){
				//					System.out.println("FocusFrame zoomLevel = "+getZoomLevel());
				//				}
			}
		});
		getCanvas().requestFocus();


		insideToolbar = new InsideToolbar_FocusFrame(this);
		getCamera().addChild(insideToolbar);

		//初期レイアウト
		sorttype = 1;
		if (satn.menutoolbar != null){
			MyAction seatmap = satn.menutoolbar.getAction("SheetMap");
			if (seatmap != null && seatmap.isSelected()){
				if (ConfigWindow.configWin.seatmap.active_smap != null){
					sorttype = 3;
				}
			}
		}

		relayout();
		zoomToBounds(tempAfterLayoutBounds);
		validate();
		if (isReference){
			toggleFocusReference();
		} else {
			if (isFullScreen) windowMaximize(); 
			else windowmanager.relativefit(tempAfterLayoutBounds);
		}

		Thread t = new Thread(this);
		t.start();
	}
	@Override
	public void run() {
		setVisible(true);
		try {
			Thread.sleep(1200);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		validate();
		relayout();
		zoomToBounds(tempAfterLayoutBounds);
	}

	public void sortByToolbarButtonStatus() {
		//ツールバーのSeatmapレイアウトボタンの状況
		boolean isSeatmap = satn.menutoolbar.getAction("SheetMap").isSelected();
		if (isSeatmap) sortBySeat(); else sortByPenID();
	}

	//TODO: 書き込み量に応じた並べ替え
	public void sortByDrawingAmount(){
		if (layoutA == null) layoutA = new LayoutByAmountHorizontal(getCanvas().getCamera());
		layoutActive = layoutA;
		layoutActive.layout(0, this);
		tempAfterLayoutBounds = layoutActive.contentregion(this);
		zoomToBounds(tempAfterLayoutBounds);
		sorttype = 2;
	}
	//TODO: 丁寧度に応じた並べ替え
	public void sortByTeinei(){
		if (layoutT == null) layoutT = new LayoutByTeinei(getCanvas().getCamera());
		layoutActive = layoutT;
		layoutActive.layout(0, this);
		tempAfterLayoutBounds = layoutActive.contentregion(this);
		zoomToBounds(tempAfterLayoutBounds);
		sorttype = 4;
	}
	public void sortBySeat() {
		//筆跡マップが有効か？
		MyAction seatmap;
		if (satn.menutoolbar != null){
			seatmap = satn.menutoolbar.getAction("SheetMap");
			if (seatmap != null && seatmap.isSelected()){
				if (ConfigWindow.configWin.seatmap.active_smap != null){
					if (layoutseat == null){
						layoutseat = new LayoutBySeat(getCanvas().getCamera(), ConfigWindow.configWin.seatmap.active_smap);
					}
					layoutseat.layout(0, this);
					tempAfterLayoutBounds = layoutseat.contentregion(this);
				}
			}
		}
		if (layoutseat != null) {
			layoutActive = layoutseat;
			layoutActive.layout(0, this);
			tempAfterLayoutBounds = layoutActive.contentregion(this);
			zoomToBounds(tempAfterLayoutBounds);
			sorttype = 3;
		}
	}

	public void sortByPenID(){
		if (layoutH == null){
			layoutH = new LayoutByIDHorizontal(getCanvas().getCamera());
		}
		layoutActive = layoutH;
		layoutActive.layout(0, this);
		tempAfterLayoutBounds = layoutActive.contentregion(this);
		zoomToBounds(tempAfterLayoutBounds);
		sorttype = 1;
	}

	public void relayout(){
		if (sorttype == 1){
			sortByPenID();
		} else if (sorttype == 2){
			sortByDrawingAmount();
		} else if (sorttype == 3){
			sortBySeat();
		} else if (sorttype == 4){
			sortByTeinei();
		}
		updateCameraView();
	}
	public void updateCameraView(){
		for(FocusRectangle fr: rects_bysid.values()){
			fr.updateCameraView();
		}
	}

	public FocusRectangle findFocusRectAtGlobalPoint(Point2D p){
		@SuppressWarnings("unchecked")
		Collection<PNode> c = getCanvas().getLayer().getAllNodes();
		for(PNode n: c){
			if (n instanceof FocusRectangle){
				FocusRectangle s = (FocusRectangle)n;
				if (s.getGlobalBounds().contains(p)){
					return s;
				}
			}
		}
		return null;
	}
	public int getZoomLevel(){
		double h = getCanvas().getCamera().getViewBounds().getHeight();
		double w = getCanvas().getCamera().getViewBounds().getWidth();
		PBounds pb = rects_bysid.get(rects_bysid.firstKey()).getGlobalFullBounds();

		PBounds gfb = getCanvas().getLayer().getGlobalFullBounds();
		//		System.out.println("diffw "+Math.abs(gfb.getWidth()-w)+ "   w "+w);
		//		System.out.println("diffh "+Math.abs(gfb.getHeight()-h)+ "   h "+h);
		if (Math.abs(gfb.getWidth()-w)<w*0.01 || Math.abs(gfb.getHeight()-h)<h*0.01) return -1; //もうレイアウト済み

		if (h > pb.getHeight() * 1.30){
			//			System.out.println("W "+w + "  w "+pb.getWidth()+"  "+Math.abs(w-pb.getWidth())+" "+w*0.1);
			if (Math.abs(w-pb.getWidth())<w*0.1 ) {
				return 1;
			}
			return 0;
		} else if (h < pb.getHeight() * 0.50){
			return 2;
		}
		return 1;
	}
	public void upview(){
		upview(getZoomLevel());
	}
	public void upview(int zoomLevel){
		if (zoomLevel == 2){
			Point2D centerP = getCanvas().getCamera().getViewBounds().getCenter2D();
			centerP = getCanvas().getCamera().localToGlobal(centerP);
			//中心点がふくまれる，Sheetを検索する．複数あったら最初のものを採用．
			FocusRectangle sheet = findFocusRectAtGlobalPoint(centerP);
			if (sheet != null){
				Rectangle2D globalb = sheet.getGlobalBounds();
				sheet.canvas.getCamera().animateViewToCenterBounds(globalb, true, 1000);
			} else {
				zoomHome(1000);
			}

			//			getCanvas().getCamera().animateViewToCenterBounds(BUtil.zoomBounds(getCanvas().getCamera().getViewBounds(), 3.0),false,1000);
		} else if (zoomLevel == -1){
			relayout();
			zoomToBounds(tempAfterLayoutBounds);
		} else { 
			zoomHome(1000);
		}
	}
	//全体画面を表示
	public void zoomHome(int msec){
		getCanvas().getCamera().animateViewToCenterBounds(getCanvas().getLayer().getGlobalFullBounds(),true, msec);//msecミリ秒かけてアニメーション
	}

	//TODO:
	public Dimension availableScreenSize(){
		Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
		return new Dimension((int)(dim.getWidth()-60)/2, (int)(dim.getHeight()-40));
	}
	public Dimension availableMaxScreenSize(){
		Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
		return new Dimension((int)(dim.getWidth()-60), (int)(dim.getHeight()-40));
	}
	public void windowMaximize(){
		if (satn.menutoolbar.getAction("Secondary Display").isSelected()){
			satn.showFrameOnSecondaryDisplay(this);
		} else {
			setSize(availableMaxScreenSize());
			setLocation(0, 0);
		}
	}
	public void windowNormalize(){
		setSize(availableScreenSize());
		setLocation(550, 0);
	}
	public void processCursorKey(int keyCode) {
		int zL = getZoomLevel();
		if (zL == 1){
			Point2D centerP = getCanvas().getCamera().getViewBounds().getCenter2D();
			centerP = getCanvas().getCamera().localToGlobal(centerP);
			//中心点がふくまれる，Sheetを検索する．複数あったら最初のものを採用．
			FocusRectangle sheet = findFocusRectAtGlobalPoint(centerP);
			if (sheet != null){
				LayoutContent nextFR = layoutActive.getUDLR(keyCode, sheet);
//				int pos = rects_sorted.indexOf(sheet);
//				int offset = 0;
//				if (keyCode == KeyEvent.VK_UP) offset = -layoutcols;
//				if (keyCode == KeyEvent.VK_DOWN) offset = layoutcols;
//				if (keyCode == KeyEvent.VK_LEFT) offset = -1;
//				if (keyCode == KeyEvent.VK_RIGHT) offset = 1;
//				FocusRectangle nextFR = getNext(pos, offset);
				if (nextFR == null) {
					zoomHome(1000);
				} else {
					nextFR.focus();
				}
			}
		} else if (zL == 2){
			PBounds pb = getCanvas().getCamera().getViewBounds();
			if (keyCode == 38) {// 上
				pb.moveBy(0, -pb.getHeight()*0.8);
				zoomToBounds(pb);
			}
			if (keyCode == 37) {// 左
				pb.moveBy(-pb.getWidth()*0.8,0);
				zoomToBounds(pb);
			}
			if (keyCode == 39) {// 右
				pb.moveBy(pb.getWidth()*0.8,0);
				zoomToBounds(pb);
			}
			if (keyCode == 40) {// 下
				pb.moveBy(0,pb.getHeight()*0.8);
				zoomToBounds(pb);
			}
		} else {
			if (keyCode == 38) {// 上
				getCanvas().getCamera().translateView(0,10);
				getCanvas().repaint();
			}
			if (keyCode == 37) {// 左
				getCanvas().getCamera().translateView(10,0);
				getCanvas().repaint();
			}
			if (keyCode == 39) {// 右
				getCanvas().getCamera().translateView(-10,0);
				getCanvas().repaint();
			}
			if (keyCode == 40) {// 下
				getCanvas().getCamera().translateView(0,-10);
				getCanvas().repaint();
			}
		}

	}
	public void zoomToBoundsWithWait(PBounds pb, int msec){
		class ZoomToBoundsTask extends TimerTask{
			public void run(){
				zoomToBounds(tempAfterLayoutBounds);
			}
		}
		Timer t = new Timer();
		t.schedule(new ZoomToBoundsTask(), msec);
	}
	public void zoomToBounds(PBounds pb) {
		zoomToBounds(pb, SimpleATN.zoommsec);
	}
	public void zoomToBounds(PBounds pb, int msec) {
		getCanvas().getCamera().animateViewToCenterBounds(pb, true, msec);
	}

	//フォーカスリファレンス
	Dimension fsize;
	Point floc;
	public boolean isFocusReferenceMode = false;
	public void toggleFocusReference() {
		isFocusReferenceMode = !isFocusReferenceMode;

		if (isFocusReferenceMode){
			fsize = getSize();
			floc = getLocation();
			setSize((int)(sheetb.getWidth()*1.4), (int)(sheetb.getHeight()*1.4));
			setLocation((int)SimpleATN.theapp.getLocation().getX(),
					(int)(SimpleATN.theapp.getLocation().getY()+SimpleATN.theapp.getSize().getHeight()+1));
		} else {
			setSize(fsize);
			setLocation(floc);
		}
	}
	public void delete(){
		dispose();
		FocusFrame.frames.remove(this);
		for(FocusRectangle fr: rects_bysid.values()){
			fr.focussrc.removeFromParent();
		}
	}

	//追加機能分のメニュー
	public void addPluginMenuTo(JPopupMenu menu) {
		for(DefaultPlugin dp: plugins){
			dp.addMenu(menu);
		}
	}
	//フォーカス from LayerSeatmapButton
	public void focus(int penid) {
		FocusRectangle fr = rects_bysid.get(penid);
		if (fr != null) zoomToBounds(fr.getGlobalBounds());

	}

	public void layoutBySeatmap() {
		if (layoutseat == null){
			if (ConfigWindow.configWin.seatmap.active_smap != null){
				layoutseat = new LayoutBySeat(getCanvas().getCamera(), ConfigWindow.configWin.seatmap.active_smap);
			}
		}
		layoutseat.layout(0, this);
		tempAfterLayoutBounds = layoutseat.contentregion(this);

	}
	@Override
	public ArrayList<LayoutContent> getLayoutContents() {
		ArrayList<LayoutContent> cnt = new ArrayList<LayoutContent>();
		cnt.addAll(rects_bysid.values());
		return cnt;
	}
}
