package jp.ac.kyutech.mns.ist;

import java.awt.Color;
import java.awt.Font;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;

import org.josql.Query;
import org.josql.QueryExecutionException;
import org.josql.QueryParseException;
import org.josql.QueryResults;

import jp.ac.kyutech.mns.ist.filter.AvgVarCalc2;
import jp.ac.kyutech.mns.ist.util.BUtil;
import pothos_tegaki.PothosLibRecognizer;
import pothos_tegaki.TegakiCallBack;
import pothos_tegaki.TegakiRecogTask;
import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.PCanvas;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.event.PBasicInputEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolo.nodes.PText;
import edu.umd.cs.piccolo.util.PBounds;

public class FocusRectangle extends PPath implements TegakiCallBack, LayoutContent {
	private static final long serialVersionUID = 89458177638758913L;
	public static Font defaultFont = new Font("SansSerif", Font.PLAIN, 30);
	public static Color defaultLabelRoundColor = Color.LIGHT_GRAY;

	public static float rlw = 20f;
	public transient PCamera cam;

	public PPath backGround;

	transient PPath handle;
	//	transient PPath highlight;
	PText pt_penid;

	Sheet sheet;
	PCanvas canvas;
	public  FocusSrc focussrc;
	public FRPalette palette;

	public Rectangle2D mysize;		//レイアウト用のサイズ．Palette含まず
	public Event_onFR event_onfr;

	public static PothosLibRecognizer recognizer = null;
	static {
		try{
//			recognizer = new PothosLibRecognizer(); //TODO:
		}catch(UnsatisfiedLinkError ue){
			recognizer = null;
		}
	}
	public Note getNote(){
		return sheet.getNote();
	}
	public Rectangle2D mysize(){
		return mysize;
	}
	public boolean hasContent(){
		return true;
	}
	public FocusRectangle(Dimension2D size, Sheet _sheet, Rectangle2D viewRegion, PCanvas _focuscanvas){
		double worh = (size.getWidth() > size.getHeight() )? size.getWidth() : size.getHeight();
		if (_sheet.getNote().isLandscapeMode){
			setPathTo(
					new RoundRectangle2D.Double(0,0,size.getHeight()+(size.getHeight()/rlw),
						size.getWidth()+(size.getHeight()/rlw),size.getWidth()/(worh/rlw),size.getWidth()/(worh/rlw)));
		} else {
			setPathTo(new RoundRectangle2D.Double(0,0,size.getWidth()+(size.getWidth()/rlw),
					size.getHeight()+(size.getWidth()/rlw),size.getHeight()/(worh/rlw),size.getHeight()/(worh/rlw)));
		}
		sheet = _sheet;
		canvas = _focuscanvas;

		this.setTransparency(1.0f);
		this.setStrokePaint(null);

		//シート上でのマウス操作
		event_onfr = new Event_onFR(this);
		addInputEventListener(event_onfr);

		backGround = new PPath(this.getPathReference());
		backGround.setTransparency(1.0f);
		backGround.setPaint(defaultLabelRoundColor);

		backGround.setStroke(Note.getCachedBasicStroke((float)(worh/(rlw*(1.0f+(rlw/worh))))));
		backGround.setStrokePaint(defaultLabelRoundColor);
		addChild(backGround);

		resetCam();
		cam.setViewBounds(viewRegion);
		cam.repaint();

		//レイアウト用のサイズ
		mysize = getFullBounds();

		palette = new FRPalette(this);
		palette.setVisible(false);
		if (cam.getWidth() < palette.getWidth()){
			palette.setScale(cam.getWidth()/palette.getWidth());
			pt_penid.setScale(cam.getWidth()/palette.getWidth());

			pt_penid.setOffset(getWidth()-pt_penid.getWidth()*pt_penid.getScale(),
					getHeight()-pt_penid.getHeight()*pt_penid.getScale());
			//			if (pt_penid.getText().equals("23")){ //for debug
			//				new DebugPanel(pt_penid,"PenID");
			//				new DebugPanel(palette,"Palette");
			//				new DebugPanel(this,"FR");
			//			}
			palette.fitToFR();
		}
	}
	public void focus(){
		focus(1000, true);
	}
	public void focus(int msec, boolean updateFRef){
		//フォーカス
		Rectangle2D globalb = getGlobalBounds();
		canvas.getCamera().animateViewToCenterBounds(globalb, true, msec);
		sheet.isshowImage(true);

		if (updateFRef) FocusFrame.updateFocusReference(sheet.getNote().penid);
	}
	public void focusReference(){
		Rectangle2D globalb = getGlobalBounds();
		canvas.getCamera().setViewBounds(globalb);
		sheet.isshowImage(true);
	}

	public void setLabelRoundColor(Color c){
		if (backGround != null) backGround.setStrokePaint(c);
	}
	public void resetCam(){
		if (cam == null){
			cam = new PCamera();

			cam.setPickable(true);
			addChild(cam);
			//			cam.setOffset(3,3);
			cam.setBounds(BUtil.plusBounds(this.getBounds(), -10, -10)); 
			cam.addAttribute("moveTarget", this);

			cam.addLayer(sheet.canvas.getLayer());
		}
		//		if (highlight == null){
		//			highlight = new PPath(this.getPathReference());
		//			highlight.setVisible(false);
		//			highlight.setPaint(Color.yellow);
		//			highlight.setStrokePaint(Color.red);
		//			highlight.setStroke(Note.getCachedBasicStroke(10f));
		//			highlight.setPickable(false);
		//			addChild(highlight);
		//			highlight.moveToFront();
		//		}
		if (handle == null){
			handle = new PPath(this.getPathReference());
			handle.addAttribute("moveTarget", this);
			handle.setTransparency(0.0f);
			handle.setPaint(Color.pink);
			handle.setStrokePaint(Color.red);
			handle.setStroke(Note.getCachedBasicStroke(10f));
			handle.setPickable(true);
			addChild(handle);
			handle.moveToFront();

			handle.addInputEventListener(new PBasicInputEventHandler(){
				public void mouseEntered(PInputEvent e){
					focussrc.isHover(true);
					palette.setVisible(true);

					FocusFrame.updateFocusReference(sheet.getNote().penid);
				}
				public void mouseExited(PInputEvent e){
					focussrc.isHover(false);
					palette.setVisible(false);
				}
			});
		}
		if (handle != null) {
			handle.moveToFront();
			//			handle.addAttribute("drawTarget", student.sheetHash.get(tagid/100).ink);
			//			handle.addAttribute("drawOffset", cam.getViewBounds().getOrigin());

			handle.addInputEventListener(new PBasicInputEventHandler(){
				public void mouseEntered(PInputEvent event){
				}
				public void mouseExited(PInputEvent event){
				}
			});
		}

		if (pt_penid == null){
			pt_penid = new PText(String.valueOf(sheet.getNote().penid));
			pt_penid.setScale(2f);
			pt_penid.setTransparency(0.6f);
			pt_penid.offset(getWidth()-pt_penid.getWidth()*pt_penid.getScale()-10, getHeight()-pt_penid.getHeight()*pt_penid.getScale()-10);
			addChild(pt_penid);
		}

	}
	public void updateSize(int size){
		setPathTo(new RoundRectangle2D.Double(-size/2,-size/2,size*2,size,size/6,size/6));
		cam.setBounds(getBounds());
		cam.setViewBounds(sheet.getGlobalBounds());
		//		cam.setViewBounds(new PBounds(0,0,700,990));
		cam.repaint();
	}

	/**
	 * フィールドに対して，任意のメッセージを投げる
	 * 例：
	 * @param target
	 * @param field
	 * @param method
	 * @param argtypes
	 * @param args
	 */
	public void callFieldMethod(String target, String field, String method, Class<?>[] argtypes, Object... args) {
		try {
			Field f = Sheet.class.getField(field);
			Object pn = f.get(this);
			Method m = pn.getClass().getMethod(method, argtypes);
			m.invoke(pn, args);				
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
	/**
	 * フィールドを引数としたメソッドを呼ぶ．ただし，フィールドはすべてPNodeで，引数は１つ
	 * 例：stu.callMethodWithFieldArg("sheet","copyrect_page","addChild");
	 * @param target
	 * @param fieldarg
	 * @param method
	 */
	public void callMethodWithFieldArg(String target, String fieldarg, String method) {
		try {
			Field f = Sheet.class.getField(fieldarg);
			Object farg = f.get(this);
			Method m = getClass().getMethod(method, farg.getClass());
			m.invoke(this, farg);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
	/**
	 * フィールドを引数としたメソッドを呼ぶ．ただし，フィールドはすべてPNodeで，引数は複数
	 * 例：stu.callMethodWithFieldArg("sheet","copyrect_page","addChild");
	 * @param target
	 * @param object
	 * @param method
	 */
	public void callMethodWithFieldMultipleArgs(String target, String[] objects, Class<?>[] argtypes, String method) {
		try {
			ArrayList<Object> pns = new ArrayList<Object>();
			for(String object: objects){
				Field f = FocusRectangle.class.getField(object);
				pns.add(f.get(this));
			}
			Method m = getClass().getMethod(method, argtypes);
			m.invoke(this, pns.toArray());
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
	public void setFocusSrc(FocusSrc _fs) {
		focussrc = _fs;
	}
	public void updateCameraView(){
		//		if (focussrc == null) return;
		PBounds pb = focussrc.getGlobalBounds();
		cam.animateViewToCenterBounds(pb, false, 1000);
	}
	//筆記量の調査
	public int drawingAmount() {
		int num = 0;
		@SuppressWarnings("unchecked")
		Collection<PNode> c = (Collection<PNode>)sheet.getAllNodes();
		Rectangle2D rect = focussrc.getBounds();
		for(PNode pn: c){
			if (rect.contains(pn.getBounds())) num++;
		}
		return num;
	}
	//文字認識
	public void recognize() {
		ArrayList<ShortStroke> ssvec = new ArrayList<ShortStroke>();
		@SuppressWarnings("unchecked")
		Collection<PNode> c = (Collection<PNode>)sheet.getAllNodes();
		Rectangle2D rect = focussrc.getBounds();
		for(PNode pn: c){
			if (pn instanceof ShortStroke){
				if (rect.contains(pn.getBounds())) {
					ssvec.add((ShortStroke)pn);
				}
			}
		}
		TegakiRecogTask trt = new TegakiRecogTask(this);
		trt.setSSVec(ssvec);
		System.out.println(recognizer.recognize(trt));
	}
	@Override
	public void callback(TegakiRecogTask s) {
		System.out.println(s.result);
	}
	@Override
	public double teineiLevel() {
		ArrayList<ShortStroke> ssvec = new ArrayList<ShortStroke>();
		@SuppressWarnings("unchecked")
		Collection<PNode> c = (Collection<PNode>)sheet.getAllNodes();
		Rectangle2D rect = focussrc.getBounds();
		for(PNode pn: c){
			if (pn instanceof ShortStroke){
				if (rect.contains(pn.getBounds())) {
					ShortStroke ss = (ShortStroke)pn;
					ss.calcMetrics();
					//ramer>-1 and pts<=30 and dist<=77
					if (ss.ramer<0) continue;
					if (ss.pts>30) continue;
					if (ss.dist>77) continue;
					ssvec.add(ss);
				}
			}
		}
		
		double m1 = varPpdR1(ssvec);
		double m2 = avgRamer(ssvec);
		double m3 = avgPpd(ssvec);
		// TODO Auto-generated method stub
		double ret = m1+m2+m3;
		System.out.println(sheet.getNote().penid+" "+ret);
		return ret;
	}
	public double varPpdR1(ArrayList<ShortStroke> ass){
		Query q1 = new Query();
		try {
			q1.parse("select * from jp.ac.kyutech.mns.ist.ShortStroke where ramer=1");
		} catch (QueryParseException e) {	e.printStackTrace();}
		QueryResults qr1=null;
		try {
			qr1 = q1.execute(ass);
		} catch (QueryExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		AvgVarCalc2<ShortStroke> calc1 = new AvgVarCalc2<ShortStroke>(new String[]{"pts","ppd","var_ramer","dist","ramer"}, (ArrayList<ShortStroke>)qr1.getResults());
		if (calc1.getcnt("ppd")<4) return 2;
		double varppd1 = calc1.getvar("ppd");
		return varppd1;
	}
	public double avgRamer(ArrayList<ShortStroke> ass){
		AvgVarCalc2<ShortStroke> calc1 = new AvgVarCalc2<ShortStroke>(new String[]{"pts","ppd","var_ramer","dist","ramer"}, ass);
		if (calc1.getcnt("ramer")<4) return 2;
		return calc1.getavg("ramer");
	}
	public double avgPpd(ArrayList<ShortStroke> ass){
		AvgVarCalc2<ShortStroke> calc1 = new AvgVarCalc2<ShortStroke>(new String[]{"pts","ppd","var_ramer","dist","ramer"}, ass);
		if (calc1.getcnt("ppd")<4) return 2;
		return calc1.getavg("ppd");
	}


}
