package jp.ac.kyutech.mns.ist;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import jp.ac.kyutech.mns.ist.filter.AvgVarCalc2;
import jp.ac.kyutech.mns.ist.util.FadeTimerForNotify;
import jp.ac.kyutech.mns.ist.util.FileReadWriter;

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

import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolo.nodes.PText;
import edu.umd.cs.piccolo.util.PBounds;

/**
 * １つのペンに対応するデータ＝ノート
 * ノート内に，６つのシートを保有する
 */

public class Note extends PPath implements LayoutContent {
	private static final long serialVersionUID = 3941932778256401748L;
	public static int width = 700;
	public static int height = 990;

	SimpleATN satn;
	Hashtable<Integer,Sheet> sheets;
	MyRect numPT;
	//	PText numPT;
	public int penid;
	private ArrayList<ShortStroke> all; //保存用，および，グルーピング用
	ArrayList<SSGroup> group;//ノート（ペン1本、6シート）全体が含むSSGroupのリスト
	Hashtable<Sheet,ArrayList<SSGroup>> sheet2ssgrouplist;

	boolean isLandscapeMode = false; //横画面モード

	public ArrayList<ShortStroke> getAll(){
		return all;
	}
	public int getNum(){
		return penid;
	}
	public Rectangle2D mysize(){
		return getFullBounds();
	}
	public int drawingAmount(){
		return all.size();
	}
	@SuppressWarnings("unchecked")
	public void grouping(float r){
		sheet2ssgrouplist = new Hashtable<Sheet, ArrayList<SSGroup>>();
		group = new ArrayList<SSGroup>();

		//		for(ShortStroke ss : all){
		//		}
		SSGroup target = new SSGroup();
		group.add(target);
		for(int i=0;i<all.size()-1; i++){
			ShortStroke ss = all.get(i);
			ShortStroke ss2 = all.get(i+1);
			if (ss.distance(ss2) < r){ //距離を測って
				target.add(ss);
			} else {
				target.add(ss);
				target = new SSGroup();
				group.add(target);
			}
		}

		for(SSGroup ssg: group){
			ssg.rebuild();
			ShortStroke temp = ssg.get(0);//グループに含まれる、最初の線をピックアップ
			if (temp == null) continue;
			Sheet sheet = getSheet(temp.paperid);//最初の線が含まれているシートに、
			sheet.addChild(ssg);//SSGroupを追加＝表示
			ssg.setSheet(sheet);
		}

		for(Sheet sheet : sheets.values()){
			Collection<PNode> c = sheet.getAllNodes();
			for(PNode pn : c){
				if (pn instanceof SSGroup){
					SSGroup thessg = (SSGroup)pn;
					ArrayList<SSGroup> sheetgroups = sheet2ssgrouplist.get(sheet);
					if (sheetgroups == null) {
						sheetgroups = new ArrayList<SSGroup>();
						sheet2ssgrouplist.put(sheet, sheetgroups);
					}
					sheetgroups.add(thessg);
				}
			}
			//			if (sheet2ssgrouplist.get(sheet) != null) System.out.println("num of ssgroup "+ sheet2ssgrouplist.get(sheet).size());
		}

	}

	public Note(int _penid, SimpleATN _satn) {
		this.penid = _penid;
		satn = _satn;
		//		numPT = new PText(String.valueOf(penid));
		//		numPT.setTextPaint(Color.black);
		numPT = new MyRect(String.valueOf(penid), (int)(Note.height), Note.height);
		addChild(numPT);
		numPT.innerText.setTextPaint(Color.lightGray);
		numPT.addInputEventListener(new Event_onSheetNum(numPT, this));
		//		numPT.setScale(50);
		//		numPT.setOffset(-numPT.getWidth()*numPT.getScale()-50, 50);
		//		setBounds(0,0,width*6+numPT.getWidth(),height);
		setStrokePaint(Color.black);
		setPaint(null);

		all = new ArrayList<ShortStroke>();

		sheets = new Hashtable<Integer, Sheet>();

		//		addSheet(1);
		//		addSheet(2);
		//		addSheet(3);
		//		addSheet(4);
		//		addSheet(5);
		//		addSheet(6);

	}

	public Sheet addSheet(int pagenum){
		//		PPath s = PPath.createRectangle(0, 0, width, height);
		Sheet s = new Sheet(this, pagenum);
		sheets.put(pagenum, s);
		s.setPathToRectangle(0, 0, width, height);
		//		s.setPaint(Color.white);
		setStrokePaint(Color.black);
		//		s.offset(numPT.getWidth()+10+(width+10)*(pagenum-1), 0); // to be refined
		//		s.scale(1); // to be refined
		addChild(s);
		if (isLandscapeMode) s.rotate(Math.PI/2);
		if (satn.sheetVisibleMode[pagenum]>0){
			s.setVisible(true);
			layoutVisibleSheets();
		} else {
			s.setVisible(false);
		}
		return s;
	}
	public boolean hasContent() {
		if (getBounds().width*getBounds().height > 10){
			return true;
		}
		return false;
	}

	public void layoutVisibleSheets(){
		PBounds pb = new PBounds();
		if (numPT.getVisible()) pb.add(numPT.getBounds());
		int rotatedWidth = width;
		if (isLandscapeMode) {
			rotatedWidth = height;
			//rotateOffsetX = height;
			//			pos++;
		}
		int x = 10; //default GAP
		if (numPT.getVisible()) x += numPT.getWidth();
		if (isLandscapeMode) x+= rotatedWidth;
		if (!numPT.getVisible() && isLandscapeMode) x = rotatedWidth;
		for(int sn=1; sn<=6;sn++){
			Sheet sht = sheets.get(sn);
			if (sht == null) continue;
			if (sht.getVisible()){
				sht.setOffset(x, 0);
				x = x + rotatedWidth;
				pb.add(sht.getFullBounds());
				addChild(sht);
			} else {
				sht.removeFromParent();
			}
		}
		setBounds(pb);
	}
	public Sheet getSheet(int pagenum){
		Sheet ret = sheets.get(pagenum);
		if (ret == null){
			return addSheet(pagenum); //なければ登録しちゃいます
		} else return ret;
	}

	//TODO:共通の認識器，フィルタ
	public static ArrayList<SSHandler> sshandler;
	static {
		sshandler = new ArrayList<SSHandler>();
	}
	public static void handleSS(ShortStroke ss){
		for(SSHandler ssh: sshandler){
			ssh.process(ss);
		}
	}
	public static void addSSHandler(SSHandler ssh){
		//既存の筆記を処理
		for(Note n: SimpleATN.theapp.notes.values()){
			for(ShortStroke ss : n.all){
				ssh.processPrevious(ss);
			}
		}
		sshandler.add(ssh);
	}

	//TODO:筆記の画面出力
	public void addStroke(ShortStroke ss) {
		addStroke(ss,false);
	}

	public void addStroke(ShortStroke ss, boolean handled) {
		if (!handled) Note.handleSS(ss);

		ss.rebuildStroke();
		//シートに貼付け
		Sheet sheet = getSheet(ss.paperid);
		sheet.addChild(ss);

		if (satn.menutoolbar.getAction("NotifyOnNew").isSelected()) sheet.notifySheet();
		ss.setPickable(false);
		//		sheet.showFadingSheetNum(5000);
		//TODO: all必要？＞保存時のみ必要
		all.add(ss);
		//		ss.debugPrint();
	}

	static Hashtable<Float, BasicStroke> penStrokeHash;
	static Hashtable<Integer, Color> colorHash;
	static {
		penStrokeHash = new Hashtable<Float, BasicStroke>();
		colorHash = new Hashtable<Integer, Color>();
	}

	public static BasicStroke getCachedBasicStroke(float f){ //f=太さ
		if (penStrokeHash.containsKey(f)){
			return penStrokeHash.get(f);
		} else {
			BasicStroke bs = new BasicStroke(
					f, BasicStroke.CAP_ROUND,
					BasicStroke.JOIN_ROUND, 90.0f);
			penStrokeHash.put(f, bs);
			return bs;
		}
	}
	public static Color getColor(int f){
		if (colorHash.containsKey(f)){
			return colorHash.get(f);
		} else {
			Color cl = new Color(f);
			colorHash.put(f, cl);
			return cl;
		}
	}

	// sheet num, visible mode (0:hide, 1: show with data, 2: show all)
	public void toggleVisible(int sn, int i) {
		if (sn == 0){
			if (i==0) {
				numPT.removeFromParent();
				numPT.setVisible(false);
			} else {
				this.addChild(numPT);
				numPT.setVisible(true);
			}
		} else {
			if (i==0){
				Sheet sht = sheets.get(sn);
				if (sht != null) sht.setVisible(false);
			} else if (i==2){
				Sheet sht = sheets.get(sn);
				if (sht == null) sht = getSheet(sn);
				sht.setVisible(true);
			} else if (i==1){
				Sheet sht = sheets.get(sn);
				if (sht != null){
					sht.setVisible(sht.getSSSize() > 0);
				}
			}
		}
		layoutVisibleSheets();
	}
	public void applySheetVisible(){
		for(int sn=0;sn<7;sn++){
			toggleVisible(sn, satn.sheetVisibleMode[sn]);
		}
	}

	public void focus() {
		//フォーカス
		Rectangle2D globalb = getGlobalFullBounds();
		satn.zoomToBounds(globalb, 1000, "Note.focus");
	}

	public void prepare_for_print(boolean isBefore) {
		if (isBefore){
			for(Sheet s: sheets.values()) s.setStrokePaint(null);
		} else {
			for(Sheet s: sheets.values()) s.setStrokePaint(Color.black);
		}		
	}

	public void queueNotifyMessage(int num) {
		PText pt = new PText(String.valueOf(penid)+"-"+String.valueOf(num));
		pt.setScale(2.0f);
		pt.setTransparency(0.7f);
		pt.setPaint(Color.yellow);
		pt.setTextPaint(Color.black);
		satn.canvas.getCamera().addChild(pt);
		new FadeTimerForNotify(pt, 2000, 1000, true);
	}

	public void setLandscapeMode(boolean b) {
		if (isLandscapeMode && b) return; //DBからの読み込み毎に，applyが有効化を図るため
		isLandscapeMode = b;
		if (b){
			for(Sheet s: sheets.values()){
				s.rotate(Math.PI/2);
			}
		} else {
			for(Sheet s: sheets.values()){
				s.setRotation(0);
			}
		}
		layoutVisibleSheets();
	}

	public void applyDrawingMode() {
		for(Sheet s: sheets.values()){
			s.setDrawMode(satn.menutoolbar.getAction("Draw").isSelected());
		}
	}

	public void exportToCSV(ArrayList<String> buffer) {
		for(ShortStroke ss: all){
			buffer.add(ss.toCsv());
		}
	}
	public void exportToCSVTeinei(ArrayList<String> buffer) {
		ArrayList<Point2D> sheetRegionCenter = new ArrayList<Point2D>();
		sheetRegionCenter.add(new Point2D.Double(548,65));//名前
		sheetRegionCenter.add(new Point2D.Double(207,260));//番号
		sheetRegionCenter.add(new Point2D.Double(490,240));//番号の右
		sheetRegionCenter.add(new Point2D.Double(211,452));//1
		sheetRegionCenter.add(new Point2D.Double(502,456));//2
		sheetRegionCenter.add(new Point2D.Double(200,630));//3
		sheetRegionCenter.add(new Point2D.Double(506,630));//4
		sheetRegionCenter.add(new Point2D.Double(184,827));//5
		sheetRegionCenter.add(new Point2D.Double(506,827));//6
		ArrayList<Point2D> sheetRegionCenter2 = new ArrayList<Point2D>();
		sheetRegionCenter2.add(new Point2D.Double(517,68));//2名前
		sheetRegionCenter2.add(new Point2D.Double(191,234));//7
		sheetRegionCenter2.add(new Point2D.Double(498,236));//8
		sheetRegionCenter2.add(new Point2D.Double(193,441));//9
		sheetRegionCenter2.add(new Point2D.Double(500,441));//10
		sheetRegionCenter2.add(new Point2D.Double(193,626));//11
		sheetRegionCenter2.add(new Point2D.Double(500,626));//12
		sheetRegionCenter2.add(new Point2D.Double(193,840));//13
		sheetRegionCenter2.add(new Point2D.Double(500,840));//14

		//		System.out.println("size of all "+all.size());
		if (all.size()==0) return;
		long beginT = all.get(0).time; //最初の時刻
		long endT = all.get(all.size()-1).time;//最後の時刻
		long duration = endT - beginT;
		LinkedList<ShortStroke> sec10 = new LinkedList<ShortStroke>();
		LinkedList<ShortStroke> sec30 = new LinkedList<ShortStroke>();
		LinkedList<ShortStroke> sec60 = new LinkedList<ShortStroke>();
		long current;
		for(ShortStroke ss: all){
			ss.calcMetrics();
			sec10.offer(ss); sec30.offer(ss); sec60.offer(ss); //追加
			current = ss.time;
			while(current - sec10.peek().time > 10000) sec10.poll();
			while(current - sec30.peek().time > 30000) sec30.poll();
			while(current - sec60.peek().time > 60000) sec60.poll();
			if (ss.dist==0) continue;//長さが0だと，points/distがInfinityになってしまうため。筆記点1
			StringBuffer sb = new StringBuffer();
			sb.append(ss.penid);
			sb.append(",");
			sb.append(ss.paperid);
			sb.append(",");
			sb.append(current-beginT); // 経過秒
			ss.current_begin = current-beginT;
			sb.append(",");
			sb.append(((double)(current-beginT))/duration);//経過秒数の全体に対する割合
			ss.current_begin_duration = ((double)(current-beginT))/duration;
			sb.append(",");
			sb.append(sec10.size());//10秒以内の筆記数
			ss.sec10size = sec10.size();
			sb.append(",");
			sb.append(sec30.size());//30秒以内の筆記数
			ss.sec30size = sec30.size();
			sb.append(",");
			sb.append(sec60.size());//60秒以内の筆記数
			ss.sec60size = sec60.size();
			sb.append(",");
			sb.append(ss.timed);
			sb.append(",");
			sb.append(ss.ramer);
			sb.append(",");
			sb.append(ss.var_ramer);//varspeed/log10(ramer)
			sb.append(",");
			sb.append(ss.ppd);//pts/length(dist)
			sb.append(",");
			sb.append(ss.pts);
			sb.append(",");
			sb.append(ss.dist);
			sb.append(",");
			sb.append(ss.sx[0]);
			sb.append(",");
			sb.append(ss.sy[0]);
			int pos = -1;
			double min = Double.MAX_VALUE;
			if (ss.paperid==1){
				int ii=0;
				for(Point2D ppp: sheetRegionCenter){
					if (ppp.distance(ss.sx[0],ss.sy[0])<min){
						min = ppp.distance(ss.sx[0],ss.sy[0]);
						pos = ii;
					}
					ii++;
				}
			} else {
				int ii=9;
				for(Point2D ppp: sheetRegionCenter2){
					if (ppp.distance(ss.sx[0],ss.sy[0])<min){
						min = ppp.distance(ss.sx[0],ss.sy[0]);
						pos = ii;
					}
					ii++;
				}				
			}
			sb.append(",");
			sb.append(pos);
			ss.pos = pos;

			if (ss.pts > 30) {
				ss.setStrokePaint(Color.cyan);
				continue; //書き損じなどのぐちゅぐちゅなので，飛ばす
			}
			if (ss.dist >= 78) {
				ss.setStrokePaint(Color.green);
				continue;
			}
			buffer.add(sb.toString());
		}
	}
	public void exportToCSVVarppdr(ArrayList<String> buffer) {
		if (all.size()==0) return;

		double[][][] score = new double[11][18][6];
		ArrayList<String> sc = FileReadWriter.getLinesList("score.csv");
		String[][] lines = new String[6][36];
		for(int i=0;i<6;i++){
			lines[i] = sc.get(i).split("\t");
		}
		for(int j=0;j<36;j++){
			int pen = Integer.parseInt(lines[0][j]);
			int pos = Integer.parseInt(lines[1][j]);
			for(int i=2;i<6;i++){
				double scd = Double.parseDouble(lines[i][j]);
				score[pen][pos][i] = scd;
			}
		}

		Query q = new Query();
		try {
			//			q.parse("select * from jp.ac.kyutech.mns.ist.ShortStroke where ramer>3;");
			q.parse("select * from jp.ac.kyutech.mns.ist.ShortStroke where ramer>-1 and pts<=30 and dist<=77 group by pos group by order pos");
			//			q.parse("select * from jp.ac.kyutech.mns.ist.ShortStroke where ramer>-1 group by pos, ramer group by order pos, ramer");
		} catch (QueryParseException e) {
			e.printStackTrace();
		}
		QueryResults qr=null;
		List<Integer> res;
		try {
			qr = q.execute(all);
			//			ArrayList<ShortStroke> res = new ArrayList<ShortStroke>(qr.getResults());
			Map<ArrayList<Integer>, ArrayList<ShortStroke>> map = qr.getGroupByResults();

			for(ArrayList<ShortStroke> ass: map.values()){
				ShortStroke fss = ass.get(0);
				//System.out.println(fss.penid+" pos:"+fss.pos+"  ram:"+fss.ramer+"  size:"+ass.size());
				if (ass.size()<4) continue; //3以下は出さない
				StringBuffer sb = new StringBuffer();
				sb.append(fss.penid);
				sb.append(",");
				sb.append(fss.pos);
				sb.append(",");

				AvgVarCalc2 calc = new AvgVarCalc2<ShortStroke>(new String[]{"pts","ppd","var_ramer","dist","ramer"}, ass);
				//calc.printAll();
				sb.append(calc.getavg("pts"));
				sb.append(",");
				sb.append(calc.getavg("dist"));
				sb.append(",");
				sb.append(calc.getavg("var_ramer"));
				sb.append(",");
				sb.append(calc.getvar("var_ramer"));
				sb.append(",");
				sb.append(calc.getavg("ppd"));
				sb.append(",");
				sb.append(calc.getvar("ppd"));
				sb.append(",");

				Query q0 = new Query();
				try {
					q0.parse("select * from jp.ac.kyutech.mns.ist.ShortStroke where ramer=0 and pos = "+fss.pos);
				} catch (QueryParseException e) {	e.printStackTrace();}
				QueryResults qr0=null;
				qr0 = q0.execute(ass);
				AvgVarCalc2<ShortStroke> calc0 = new AvgVarCalc2<ShortStroke>(new String[]{"pts","ppd","var_ramer","dist","ramer"}, (ArrayList<ShortStroke>)qr0.getResults());

				Query q1 = new Query();
				try {
					q1.parse("select * from jp.ac.kyutech.mns.ist.ShortStroke where ramer=1 and pos = "+fss.pos);
				} catch (QueryParseException e) {	e.printStackTrace();}
				QueryResults qr1=null;
				qr1 = q1.execute(ass);
				AvgVarCalc2<ShortStroke> calc1 = new AvgVarCalc2<ShortStroke>(new String[]{"pts","ppd","var_ramer","dist","ramer"}, (ArrayList<ShortStroke>)qr1.getResults());

				Query q2 = new Query();
				try {
					q2.parse("select * from jp.ac.kyutech.mns.ist.ShortStroke where ramer=2 and pos = "+fss.pos);
				} catch (QueryParseException e) {	e.printStackTrace();}
				QueryResults qr2=null;
				qr2 = q2.execute(ass);
				AvgVarCalc2<ShortStroke> calc2 = new AvgVarCalc2<ShortStroke>(new String[]{"pts","ppd","var_ramer","dist","ramer"}, (ArrayList<ShortStroke>)qr2.getResults());

				Query q3 = new Query();
				try {
					q3.parse("select * from jp.ac.kyutech.mns.ist.ShortStroke where ramer=3 and pos = "+fss.pos);
				} catch (QueryParseException e) {	e.printStackTrace();}
				QueryResults qr3=null;
				qr3 = q3.execute(ass);
				AvgVarCalc2<ShortStroke> calc3 = new AvgVarCalc2<ShortStroke>(new String[]{"pts","ppd","var_ramer","dist","ramer"}, (ArrayList<ShortStroke>)qr3.getResults());

				Query q4 = new Query();
				try {
					q4.parse("select * from jp.ac.kyutech.mns.ist.ShortStroke where ramer=4 and pos = "+fss.pos);
				} catch (QueryParseException e) {	e.printStackTrace();}
				QueryResults qr4=null;
				qr4 = q4.execute(ass);
				AvgVarCalc2<ShortStroke> calc4 = new AvgVarCalc2<ShortStroke>(new String[]{"pts","ppd","var_ramer","dist","ramer"}, (ArrayList<ShortStroke>)qr4.getResults());

				Query q5 = new Query();
				try {
					q5.parse("select * from jp.ac.kyutech.mns.ist.ShortStroke where ramer=5 and pos = "+fss.pos);
				} catch (QueryParseException e) {	e.printStackTrace();}
				QueryResults qr5=null;
				qr5 = q5.execute(ass);
				AvgVarCalc2<ShortStroke> calc5 = new AvgVarCalc2<ShortStroke>(new String[]{"pts","ppd","var_ramer","dist","ramer"}, (ArrayList<ShortStroke>)qr5.getResults());

				double varppd0 = calc0.getvar("ppd");
				double varppd1 = calc1.getvar("ppd");
				double varppd2 = calc2.getvar("ppd");
				double varppd3 = calc3.getvar("ppd");
				double varppd4 = calc4.getvar("ppd");
				double varppd5 = calc5.getvar("ppd");
				if (calc0.getcnt("ppd")<4) varppd0 = 0;
				if (calc1.getcnt("ppd")<4) varppd1 = 0;
				if (calc2.getcnt("ppd")<4) varppd2 = 0;
				if (calc3.getcnt("ppd")<4) varppd3 = 0;
				if (calc4.getcnt("ppd")<4) varppd4 = 0;
				if (calc5.getcnt("ppd")<4) varppd5 = 0;

				sb.append(varppd0);
				sb.append(",");
				sb.append(varppd1);
				sb.append(",");
				sb.append(varppd2);
				sb.append(",");
				sb.append(varppd3);
				sb.append(",");
				sb.append(varppd4);
				sb.append(",");
				sb.append(varppd5);
				sb.append(",");
				sb.append(calc.getcnt("ppd"));
				sb.append(",");
				sb.append(calc0.getcnt("ppd"));
				sb.append(",");
				sb.append(calc1.getcnt("ppd"));
				sb.append(",");
				sb.append(calc2.getcnt("ppd"));
				sb.append(",");
				sb.append(calc3.getcnt("ppd"));
				sb.append(",");
				sb.append(calc4.getcnt("ppd"));
				sb.append(",");
				sb.append(calc5.getcnt("ppd"));
				sb.append(",");

				sb.append(calc.getavg("ramer"));
				sb.append(",");
				sb.append(calc.getvar("ramer"));
				if (score[fss.penid][fss.pos][2]>0){
					for(int i=2;i<6;i++){
						sb.append(",");
						sb.append(score[fss.penid][fss.pos][i]);
					}
					buffer.add(sb.toString());
				}
			}
		} catch (QueryExecutionException e) {
			e.printStackTrace();
		}

	}
	@Override
	public double teineiLevel() {
		// TODO Auto-generated method stub
		return 0;
	}

}
