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.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.PixelGrabber;
import java.util.ArrayList;
import java.util.List;

import smart_gs.drawing_tool.ExLine2D;
import smart_gs.drawing_tool.LineSegEditorCanvas;
import smart_gs.drawing_tool.view.LineView;
import smart_gs.logical.LineSegmentForEdit;
import smart_gs.smleditor.swingui.LineSegEditor;
import smart_gs.swingui.GSMouseEvent;

public class LineSegEditorSemiAutomaticMode extends Frame implements LineSegEditorMode{

	private static LineSegEditorSemiAutomaticMode singleton = new LineSegEditorSemiAutomaticMode();

	private Point2D start;
	private Point2D end;
	private List<Point2D> polygon;
	private Rectangle2D rect;	// (rectangle for automatic sparation)
	private List<ExLine2D> lines;
	private List<ExLine2D> separateLines;	// separated lines (only for horizontal now)
	private LineSegEditor editor;
	private boolean isDrawing = false;

	/**
	 * @return the editor
	 */
	public LineSegEditor getEditor() {
		return editor;
	}
	/**
	 * @param editor the editor to set
	 */
	public void setEditor(LineSegEditor editor) {
		this.editor = editor;
	}
	private LineSegEditorSemiAutomaticMode() {
		this.lines = new ArrayList<ExLine2D>();
		this.polygon = new ArrayList<Point2D>();
		this.separateLines = new ArrayList<ExLine2D>();
	}
	public static LineSegEditorSemiAutomaticMode getInstance(){
		return singleton;
	}

	public void mousePressed(GSMouseEvent e, LineSegEditorCanvas canvas) {
		setCursor(canvas);
		if (e.getButton() != MouseEvent.BUTTON1) {
			this.cancel();
			return;
		}
		if(!isDrawing){
			this.start = e.getPoint();
			this.end = e.getPoint();
			this.polygon.add(this.start);
			isDrawing = true;
			return;
		}
		this.end = e.getPoint();
		this.lines.add(new ExLine2D(start,end));
		this.polygon.add(this.end);
		this.start = this.end;

		if (e.getClickCount() == 2) {
			// determine polygon
			automaticSeparation(canvas);
			isDrawing = false;

			this.lines = new ArrayList<ExLine2D>();
			this.polygon = new ArrayList<Point2D>();
			this.separateLines = new ArrayList<ExLine2D>();

		}
	}

	public void mouseClicked(GSMouseEvent e,LineSegEditorCanvas canvas) {
		setCursor(canvas);

	}

	private void automaticSeparation(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.polygon) {
			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();
		}
		this.rect = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
		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];
		Image image = editor.getSpread().getImage();	// whole image
		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;
		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();
					editor.pushLinestack();
					canvas.addLineSegmentForEdit(new LineSegmentForEdit(autoLinePolygon, this.editor.getSpread().getLineDirection(), nextIndex));

					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();
		editor.pushLinestack();
		canvas.addLineSegmentForEdit(new LineSegmentForEdit(autoLinePolygon, this.editor.getSpread().getLineDirection(), nextIndex));

	}

	public void mouseMoved(GSMouseEvent e,LineSegEditorCanvas canvas) {
		setCursor(canvas);
		this.end = e.getPoint();
		setCursor(canvas);
	}

	@Override
	public void paint(Graphics g,LineSegEditorCanvas canvas) {
		double ratio = canvas.getLineSegEditorImageLabel().getRatio();
		double gapX = canvas.getLineSegEditorImageLabel().getGapWidth();
		double gapY = canvas.getLineSegEditorImageLabel().getGapHeight();
		for(int i=0;i<this.lines.size();i++){
			ExLine2D line = this.lines.get(i);
			new LineView(line).enlargedView(ratio,gapX,gapY).draw((Graphics2D)g);
		}

		if (isDrawing) {
			new LineView(new ExLine2D(start,end)).enlargedView(ratio,gapX,gapY).draw((Graphics2D)g);
		}
		/*
		if (this.separateLines != null) {
			for(int i=0;i<this.separateLines.size();i++){
				ExLine2D line = this.separateLines.get(i);
				new LineView(line).enlargedView(ratio,gapX,gapY).draw((Graphics2D)g);
			}
		}
		*/
		setCursor(canvas);
	}

	public void cancel() {
	}

	public void mouseDragged(GSMouseEvent e, LineSegEditorCanvas canvas) {
		setCursor(canvas);
	}

	public void mouseEntered(GSMouseEvent e, LineSegEditorCanvas canvas) {
		setCursor(canvas);
	}

	public void mouseExited(GSMouseEvent e, LineSegEditorCanvas canvas) {
		setCursor(canvas);
	}

	public void mouseReleased(GSMouseEvent e, LineSegEditorCanvas canvas) {
		setCursor(canvas);
	}

	public void setCursor(LineSegEditorCanvas canvas) {
		canvas.getLineSegEditorImageLabel().setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
	}

}
