package smart_gs.drawing_tool.drawing_mode;

import java.awt.Cursor;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;


import smart_gs.drawing_tool.ExLine2D;
import smart_gs.drawing_tool.LineSegEditorCanvas;
import smart_gs.drawing_tool.drawing_mode.LineSegEditorDrawRectangleMode.State;
import smart_gs.drawing_tool.view.LineView;
import smart_gs.drawing_tool.view.RectangleView;
import smart_gs.drawing_tool.view.View;
import smart_gs.logical.LineSegmentForEdit;
import smart_gs.smleditor.swingui.LineSegEditor;
import smart_gs.swingui.GSMouseEvent;
import smart_gs.util.ImageCropper;
import sml_editor.logical.LineDirection;

public class LineSegEditorSemiAutomaticMode extends Frame implements LineSegEditorMode{

//	private Point2D start;
//	private Point2D end;
	private List<Point2D> polygonAsRectToSeparate;
	private Rectangle2D rect;	// (rectangle for automatic separation)
	private List<ExLine2D> lines;
	private List<ExLine2D> separateLines;	
//	private LineSegEditor editor;
//	private boolean isDrawing = false;
	
	
	enum State {FIRST_CORNER, SECOND_CORNER, TO_FIX};

	private State state = State.FIRST_CORNER;
	private Point2D first=null;
	private Point2D second=null;
	private View firstVertical = null;
	private View firstHorizontal = null;
	private View secondVertical = null;
	private View secondHorizontal = null;
	private View rectangle = null;
	private LineSegEditor editor;
	private LineSegEditorCanvas canvas;
	private int height;
	private int width;
	//	For touch panel
	private double rectangle_x=0, rectangle_y=0, rectangle_width=0, rectangle_height=0;
	//	For mouse UI
	private Point2D start = null, end = null;
	double mouse_rect_x=-100, mouse_rect_y=0, mouse_rect_width=0, mouse_rect_height=0;


	public LineSegEditorSemiAutomaticMode(LineSegEditor editor) {
		this.editor = editor;
		this.lines = new ArrayList<ExLine2D>();
		this.polygonAsRectToSeparate = new ArrayList<Point2D>();
		this.separateLines = new ArrayList<ExLine2D>();
		this.canvas = editor.getLineSegEditorCanvas();
		this.height = canvas.getImageIcon().getImage().getHeight(null);
		this.width = canvas.getImageIcon().getImage().getWidth(null);
	}
	
	@Override
	public void mouseClicked(GSMouseEvent e, LineSegEditorCanvas canvas) {
		setCursor(canvas);
		
		if (e.getButton() != MouseEvent.BUTTON1) {
			return;
		}
	
		if (state == State.FIRST_CORNER) {
			if (first == null) {
				this.first = e.getPoint();
				this.state = State.SECOND_CORNER;
			} 
		} else if (state == State.SECOND_CORNER) {
			if(first != null) {
				this.second = e.getPoint();

				rectangle_x = Math.min(first.getX(), second.getX());
				rectangle_y = Math.min(first.getY(), second.getY());
				rectangle_width = Math.abs(first.getX()-second.getX());
				rectangle_height = Math.abs(first.getY()-second.getY());
				
				this.rectangle = new RectangleView(new Rectangle2D.Double(rectangle_x,rectangle_y,rectangle_width,rectangle_height));
				
				this.state = State.TO_FIX;
			}
		} else if (state == State.TO_FIX) {
			if (this.rectangle.contains(e.getPoint2D())) {
				editor.pushUndoStack();
				this.polygonAsRectToSeparate = createRectLines();
				automaticSeparation(canvas);
			} 
			initialize();
		}
		
		if (first != null) createFirstCoordinate(first);
		if (second != null) createSecondCoordinate(second);
		this.editor.invalidate();
		this.editor.validate();
		this.editor.repaint(); 
	}
	
	private List<Point2D> createRectLines() {
		List<Point2D> rectLines = new ArrayList<Point2D>();
		rectLines.add(new Point2D.Double(this.rectangle_x,this.rectangle_y));
		rectLines.add(new Point2D.Double(this.rectangle_x+this.rectangle_width,this.rectangle_y));
		rectLines.add(new Point2D.Double(this.rectangle_x+this.rectangle_width,this.rectangle_y+this.rectangle_height));
		rectLines.add(new Point2D.Double(this.rectangle_x,this.rectangle_y+this.rectangle_height));
		return rectLines;
	}
	
	private List<Point2D> createRectLinesForMouseUI() {
		List<Point2D> rectLines = new ArrayList<Point2D>();
		double rectangle_x, rectangle_y, rectangle_height, rectangle_width;
		rectangle_x = Math.min(start.getX(), end.getX());
		rectangle_y = Math.min(start.getY(), end.getY());
		rectangle_width = Math.abs(start.getX()-end.getX());
		rectangle_height = Math.abs(start.getY()-end.getY());
		rectLines.add(new Point2D.Double(rectangle_x,rectangle_y));
		rectLines.add(new Point2D.Double(rectangle_x+rectangle_width,rectangle_y));
		rectLines.add(new Point2D.Double(rectangle_x+rectangle_width,rectangle_y+rectangle_height));
		rectLines.add(new Point2D.Double(rectangle_x,rectangle_y+rectangle_height));
		return rectLines;
	}
	
	private void initialize() {
		this.first = null;
		this.second = null;
		this.firstVertical = null;
		this.firstHorizontal = null;
		this.secondVertical = null;
		this.secondHorizontal = null;
		this.state = State.FIRST_CORNER;
		this.rectangle = null;
		this.start = null;
		this.end = null;
		this.mouse_rect_x = -100;
		
		this.lines = new ArrayList<ExLine2D>();
		this.polygonAsRectToSeparate = new ArrayList<Point2D>();
		this.separateLines = new ArrayList<ExLine2D>();
	}
	

	private void createFirstCoordinate(Point2D point) {
		firstHorizontal = new LineView(new ExLine2D(new Point2D.Double(0,point.getY()),new Point2D.Double(this.width,point.getY())));	
		firstVertical = new LineView(new ExLine2D(new Point2D.Double(point.getX(),0),new Point2D.Double(point.getX(),this.height)));
	}

	private void createSecondCoordinate(Point2D point) {
		secondHorizontal = new LineView(new ExLine2D(new Point2D.Double(0,point.getY()),new Point2D.Double(this.width,point.getY())));
		secondVertical = new LineView(new ExLine2D(new Point2D.Double(point.getX(),0),new Point2D.Double(point.getX(),this.height)));
	}

	@Override
	public void mouseDragged(GSMouseEvent e, LineSegEditorCanvas canvas) {
		if (start == null) return;
		Point2D current = e.getPoint2D();
		this.mouse_rect_x = Math.min(start.getX(), current.getX());
		this.mouse_rect_y = Math.min(start.getY(), current.getY());
		this.mouse_rect_width = Math.abs(start.getX()-current.getX());
		this.mouse_rect_height = Math.abs(start.getY()-current.getY());
	}

	@Override
	public void mouseEntered(GSMouseEvent e, LineSegEditorCanvas canvas) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseExited(GSMouseEvent e, LineSegEditorCanvas canvas) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseMoved(GSMouseEvent e, LineSegEditorCanvas canvas) {

	}

	@Override
	public void mousePressed(GSMouseEvent e, LineSegEditorCanvas canvas) {
		this.start = e.getPoint2D();
	}

	@Override
	public void mouseReleased(GSMouseEvent e, LineSegEditorCanvas canvas) {
		this.end = e.getPoint2D();
		
		if (this.end.distance(this.start) < 100.0d) return;

		editor.pushUndoStack();
		this.polygonAsRectToSeparate = createRectLinesForMouseUI();

		automaticSeparation(canvas);
		
		this.editor.invalidate();
		this.editor.validate();
		this.editor.repaint(); 
	}

	@Override
	public void paint(Graphics g, LineSegEditorCanvas canvas) {
		double ratio = canvas.getLineSegEditorImageLabel().getRatio();
		double gapX = canvas.getLineSegEditorImageLabel().getGapWidth();
		double gapY = canvas.getLineSegEditorImageLabel().getGapHeight();

		if (this.firstHorizontal != null) {
			this.firstHorizontal.enlargedView(ratio,gapX,gapY).draw((Graphics2D)g);
		} 
		if (this.secondHorizontal != null) {
			this.secondHorizontal.enlargedView(ratio,gapX,gapY).draw((Graphics2D)g);
		} 
		if (this.firstVertical != null) {
			this.firstVertical.enlargedView(ratio,gapX,gapY).draw((Graphics2D)g);
		} 
		if (this.secondVertical != null) {
			this.secondVertical.enlargedView(ratio,gapX,gapY).draw((Graphics2D)g);
		} 
		
		if (this.firstHorizontal  == null && this.secondVertical == null && mouse_rect_x != -100) {
			LineView line;
			Point2D p1, p2;
			
			p1 = new Point2D.Double(this.mouse_rect_x,this.mouse_rect_y);
			p2 = new Point2D.Double(this.mouse_rect_x+this.mouse_rect_width,this.mouse_rect_y);	
			line = new LineView(new ExLine2D(p1,p2));
			line.enlargedView(ratio,gapX,gapY).draw((Graphics2D)g);
			
			p1 = p2;
			p2 = new Point2D.Double(this.mouse_rect_x+this.mouse_rect_width,this.mouse_rect_y+this.mouse_rect_height);	
			line = new LineView(new ExLine2D(p1,p2));
			line.enlargedView(ratio,gapX,gapY).draw((Graphics2D)g);
			
			p1 = p2;
			p2 = new Point2D.Double(this.mouse_rect_x,this.mouse_rect_y+this.mouse_rect_height);		
			line = new LineView(new ExLine2D(p1,p2));
			line.enlargedView(ratio,gapX,gapY).draw((Graphics2D)g);
			
			p1 = p2;
			p2 = new Point2D.Double(this.mouse_rect_x,this.mouse_rect_y);	
			line = new LineView(new ExLine2D(p1,p2));
			line.enlargedView(ratio,gapX,gapY).draw((Graphics2D)g);
		}

		setCursor(canvas);
	}
	
	private void automaticSeparation(LineSegEditorCanvas canvas) {
		if (editor.getSpread().getLineDirection() == LineDirection.HORIZONTAL) {
			automaticSeparationHorizontal(canvas);
		} else {
			automaticSeparationVertical(canvas);
		}
	}


	private void automaticSeparationHorizontal(LineSegEditorCanvas canvas) {
		// determine rectangle that includes the polygon
		double minX = Double.MAX_VALUE;
		double minY = Double.MAX_VALUE;
		double maxX = 0;
		double maxY = 0;
		for (Point2D point : this.polygonAsRectToSeparate) {
			if (minX > point.getX()) minX = point.getX();
			if (minY > point.getY()) minY = point.getY();
			if (maxX < point.getX()) maxX = point.getX();
			if (maxY < point.getY()) maxY = point.getY();
		}

		Image image = canvas.getImageIcon().getImage();	// whole image
		
		automaticSeparationHorizontalBody(canvas, image, minX, minY, maxX, maxY);

		this.editor.rewriteLineSegIndexes();
		initialize();
	}
	
	private void automaticSeparationHorizontalBody(LineSegEditorCanvas canvas, Image image,
			double minX, double minY, double maxX, double maxY) {
		this.rect = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
		
		BufferedImage cropped = (BufferedImage)(new ImageCropper(image,rect)).crop();

		try {
			  ImageIO.write(cropped, "jpeg", new File("c://home/sample.jpeg"));
			} catch (Exception e) {
			  e.printStackTrace();
			}
		
		this.separateLines.add(new ExLine2D(new Point2D.Double(minX, minY), new Point2D.Double(maxX, minY)));
		// horizontal projection (horizontal mode)
		int width = (int)rect.getWidth();
		int height = (int)rect.getHeight();
		int[] colsum = new int[height];

		PixelGrabber pg = new PixelGrabber(image, (int)minX, (int)minY, width, height, true);	// express the rectangle region

		try {
			pg.grabPixels();
		} catch (Exception e) {
			e.printStackTrace();
		}
		int px[] = (int[])pg.getPixels();	// ps.length = width * height
		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				int color = px[y * width + x];
				int red = (color >> 16) & 0xff;
				int green = (color >> 8) & 0xff;
				int blue = color & 0xff;
				colsum[y] += red + green + blue;
			}
		}

		// smoothing colsum
		int[] smoothed = new int[height];
		for (int i = 2; i < height - 2; i++) {
			smoothed[i] = (colsum[i - 2] + colsum[i - 1] + colsum[i] + colsum[i + 1] + colsum[i + 2]) / 5;
		}
		for(int i = 2; i < height - 2; i++){
			colsum[i]=smoothed[i];
		}

		double avgcolsum=0;
		for (int x = 0; x < height;x++) avgcolsum += colsum[x];
		avgcolsum /= height;

		int maxcolsum=Integer.MAX_VALUE;
		int maxcolidx= -1; // don't cut when the first zeroup
		int prevzeroup=-1;
		int MINWIDTH=0; // ignore the zeroup point shorter than MINWIDTH
		int scaledy;

		this.editor.pushUndoStack();
		for (int i = 1; i < height; i++) {
			if (colsum[i] > maxcolsum) {
				maxcolsum = colsum[i];
				maxcolidx = i;
			}
			if (colsum[i - 1] < avgcolsum && colsum[i] >= avgcolsum && i > prevzeroup + MINWIDTH) {
				// find zeroup
				prevzeroup=i;
				if (maxcolidx > 0) {
					//horizontal mode
					scaledy = (int) (minY + maxcolidx);
					Point2D p1 = new Point2D.Double(minX, scaledy);
					Point2D p2 = new Point2D.Double(maxX, scaledy);

					// Determine auto line polygon.
					List<Point2D> autoLinePolygon = new ArrayList<Point2D>();
					autoLinePolygon.add(this.separateLines.get(this.separateLines.size() - 1).getP1());
					autoLinePolygon.add(this.separateLines.get(this.separateLines.size() - 1).getP2());
					autoLinePolygon.add(p2);
					autoLinePolygon.add(p1);
					int nextIndex = canvas.getLines().size();
					canvas.addLineSegmentForEdit(new LineSegmentForEdit(this.editor, autoLinePolygon, this.editor.getSpread().getLineDirection()));

					this.separateLines.add(new ExLine2D(p1, p2));

				}
				maxcolsum=0;
			}
		}
		List<Point2D> autoLinePolygon = new ArrayList<Point2D>();
		autoLinePolygon.add(this.separateLines.get(this.separateLines.size() - 1).getP1());
		autoLinePolygon.add(this.separateLines.get(this.separateLines.size() - 1).getP2());
		autoLinePolygon.add(new Point2D.Double(maxX, maxY));
		autoLinePolygon.add(new Point2D.Double(minX, maxY));
		int nextIndex = canvas.getLines().size();
		canvas.addLineSegmentForEdit(new LineSegmentForEdit(this.editor, autoLinePolygon, this.editor.getSpread().getLineDirection()));
	}
	
	private void automaticSeparationVertical(LineSegEditorCanvas canvas) {
		// determine rectangle that includes the polygon
		double minX = Double.MAX_VALUE;
		double minY = Double.MAX_VALUE;
		double maxX = 0;
		double maxY = 0;
		for (Point2D point : this.polygonAsRectToSeparate) {
			if (minX > point.getX()) minX = point.getX();
			if (minY > point.getY()) minY = point.getY();
			if (maxX < point.getX()) maxX = point.getX();
			if (maxY < point.getY()) maxY = point.getY();
		}

		Image image = canvas.getImageIcon().getImage();	// whole image
		
		automaticSeparationVerticalBody(canvas, image, minX, minY, maxX, maxY);

		this.editor.rewriteLineSegIndexes();
		initialize();
	}
	
	private void automaticSeparationVerticalBody(LineSegEditorCanvas canvas, Image image,
			double minX, double minY, double maxX, double maxY) {
		this.rect = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
		
		BufferedImage cropped = (BufferedImage)(new ImageCropper(image,rect)).crop();

		try {
			  ImageIO.write(cropped, "jpeg", new File("c://home/sample.jpeg"));
			} catch (Exception e) {
			  e.printStackTrace();
			}
		
//		this.separateLines.add(new ExLine2D(new Point2D.Double(minX, minY), new Point2D.Double(maxX, minY)));
		this.separateLines.add(new ExLine2D(new Point2D.Double(maxX, minY), new Point2D.Double(maxX, maxY)));
////		 horizontal projection (horizontal mode)
//		 vertical projection (vertical mode)
		int width = (int)rect.getWidth();
		int height = (int)rect.getHeight();
//		int[] colsum = new int[height];
		int[] colsum = new int[width];
		
		PixelGrabber pg = new PixelGrabber(image, (int)minX, (int)minY, width, height, true);	// express the rectangle region

		try {
			pg.grabPixels();
		} catch (Exception e) {
			e.printStackTrace();
		}
		int px[] = (int[])pg.getPixels();	// ps.length = width * height
		for (int x = 0; x < width; x++) {
//		for (int y = 0; y < height; y++) {
			for (int y = 0; y < height; y++) {
//			for (int x = 0; x < width; x++) {
				int color = px[y * width + x];
				int red = (color >> 16) & 0xff;
				int green = (color >> 8) & 0xff;
				int blue = color & 0xff;
				colsum[x] += red + green + blue;
			}
		}

		// smoothing colsum
		int[] smoothed = new int[width];
		for (int i = 2; i < width - 2; i++) {
			smoothed[i] = (colsum[i - 2] + colsum[i - 1] + colsum[i] + colsum[i + 1] + colsum[i + 2]) / 5;
		}
		for(int i = 2; i < width - 2; i++){
			colsum[i]=smoothed[i];
		}

		double avgcolsum=0;
		for (int x = 0; x < width;x++) avgcolsum += colsum[x];
		avgcolsum /= width;

		int maxcolsum=Integer.MAX_VALUE;
		int maxcolidy=width; // don't cut when the first zeroup
		int prevzeroup=width;
		int MINWIDTH=0; // ignore the zeroup point shorter than MINWIDTH
		int scaledx;

		this.editor.pushUndoStack();
		for (int i = width - 2; i >=0 ; i--) {
			if (colsum[i] > maxcolsum) {
				maxcolsum = colsum[i];
				maxcolidy = i;
			}
			if (colsum[i + 1] < avgcolsum && colsum[i] >= avgcolsum && i < prevzeroup - MINWIDTH) {
				// find zeroup
				prevzeroup=i;
				if (maxcolidy < width) {
					//horizontal mode
					scaledx = (int) (maxX - (width - maxcolidy));
					Point2D p1 = new Point2D.Double(scaledx, minY);
					Point2D p2 = new Point2D.Double(scaledx, maxY);

					// Determine auto line polygon.
					List<Point2D> autoLinePolygon = new ArrayList<Point2D>();
					autoLinePolygon.add(this.separateLines.get(this.separateLines.size() - 1).getP1());
					autoLinePolygon.add(this.separateLines.get(this.separateLines.size() - 1).getP2());
					autoLinePolygon.add(p2);
					autoLinePolygon.add(p1);
					canvas.addLineSegmentForEdit(new LineSegmentForEdit(this.editor, autoLinePolygon, this.editor.getSpread().getLineDirection()));

					this.separateLines.add(new ExLine2D(p1, p2));

				}
				maxcolsum=0;
			}
		}
		List<Point2D> autoLinePolygon = new ArrayList<Point2D>();
		autoLinePolygon.add(this.separateLines.get(this.separateLines.size() - 1).getP1());
		autoLinePolygon.add(this.separateLines.get(this.separateLines.size() - 1).getP2());
		autoLinePolygon.add(new Point2D.Double(minX, maxY));
		autoLinePolygon.add(new Point2D.Double(minX, minY));

		canvas.addLineSegmentForEdit(new LineSegmentForEdit(this.editor, autoLinePolygon, this.editor.getSpread().getLineDirection()));
	}

	
	public void cancel() {
	}

	public void setCursor(LineSegEditorCanvas canvas) {
		canvas.getLineSegEditorImageLabel().setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
	}
	@Override
	public LineSegEditor getParentLinesegEditor() {
		// TODO Auto-generated method stub
		return null;
	}
	@Override
	public void setParentLinesegEditor() {
		// TODO Auto-generated method stub
		
	}

}
