package smart_gs.drawing_tool.drawing_mode;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JOptionPane;

import smart_gs.drawing_tool.SpreadCanvas;
import smart_gs.drawing_tool.state.DefaultState;
import smart_gs.image_search.logical.TextType;
import smart_gs.logical.LineSegment;
import smart_gs.logical.Spread;
import smart_gs.logical.region.PolygonRegion;
import smart_gs.swingui.GSMouseEvent;
import smart_gs.swingui.toolbar.ImageToolBar;
import smart_gs.util.ImageToolBarSetModeByMouseEvent;
import sml_editor.logical.LineDirection;

public class LinesMode extends DrawingModeAdapter {

	private static Mode singleton = new LinesMode();

	private Point2D start;
	private Point2D end;

	private boolean temporary = false;

	private List<Point2D> points;

	private Component startcomponent;
	private boolean out = false;


	private LinesMode() {
		this.points = new ArrayList<Point2D>();
	}
	public static Mode getInstance(){
		return singleton;
	}

	@Override
	public boolean isTemporary() {
		return temporary;
	}
	@Override
	public void setTemporary(boolean sw) {
		temporary = sw;
	}

	@Override
	public void mousePressed(GSMouseEvent e, SpreadCanvas canvas) {
		if (start == null) startcomponent = null;
		if(startcomponent != null && (e.getComponent() != startcomponent)){
			setCursor(canvas);
			return ;
		}
		super.mousePressed(e, canvas);

		if (e.getButton() != MouseEvent.BUTTON1) {
			this.cancel();
			setCursor(canvas);
			return;
		}

		if(this.start == null){
			startcomponent = e.getComponent();
			this.start = e.getPoint();
			setCursor(canvas);
			return;
		} else if (this.end == null){
			end = e.getPoint();

			int startIndex = checkLineIndex(start, canvas);
			int endIndex = checkLineIndex(end, canvas);
			if (startIndex < 0 || endIndex < 0) {
				// show error dialog and restart
				JOptionPane.showMessageDialog(null, "start/end point is not in a line segment!");
				start = null;
				end = null;
				setCursor(canvas);
				return;
				
			}
			
			Spread spread = canvas.getCurrentSpread();
			List<LineSegment> allLines = spread.getLines();

			if (spread.getLineDirection() == LineDirection.HORIZONTAL) {	// horizontal
				double minX = Double.MAX_VALUE;
				double maxX = 0;
				for (int i = startIndex; i < endIndex; i++) {
					double tmp = allLines.get(i).getPolygon().getBounds().getMinX();
					if (minX > tmp) {
						minX = tmp;
					}
					tmp = allLines.get(i).getPolygon().getBounds().getMaxX();
					if (maxX < tmp) {
						maxX = tmp;
					}
				}
				LineSegment startLine = allLines.get(startIndex);
				LineSegment endLine = allLines.get(endIndex);
				Rectangle startBounds = startLine.getPolygon().getBounds();
				Rectangle endBounds = endLine.getPolygon().getBounds();
	
				points.add(new Point2D.Double(start.getX(), startBounds.getMinY()));
				points.add(new Point2D.Double(start.getX(), startBounds.getMaxY()));
				points.add(new Point2D.Double(minX, startBounds.getMaxY()));
				points.add(new Point2D.Double(minX, endBounds.getMaxY()));
				points.add(new Point2D.Double(end.getX(), endBounds.getMaxY()));
				points.add(new Point2D.Double(end.getX(), endBounds.getMinY()));
				points.add(new Point2D.Double(maxX, endBounds.getMinY()));
				points.add(new Point2D.Double(maxX, startBounds.getMinY()));
			} else {	// vertical
				double minY = Double.MAX_VALUE;
				double maxY = 0;
				for (int i = startIndex; i < endIndex; i++) {
					double tmp = allLines.get(i).getPolygon().getBounds().getMinY();
					if (minY > tmp) {
						minY = tmp;
					}
					tmp = allLines.get(i).getPolygon().getBounds().getMaxY();
					if (maxY < tmp) {
						maxY = tmp;
					}
				}
				LineSegment startLine = allLines.get(startIndex);
				LineSegment endLine = allLines.get(endIndex);
				Rectangle startBounds = startLine.getPolygon().getBounds();
				Rectangle endBounds = endLine.getPolygon().getBounds();
	
				points.add(new Point2D.Double(startBounds.getMaxX(), start.getY()));
				points.add(new Point2D.Double(startBounds.getMinX(), start.getY()));
				points.add(new Point2D.Double(startBounds.getMinX(), minY));
				points.add(new Point2D.Double(endBounds.getMinX(), minY));
				points.add(new Point2D.Double(endBounds.getMinX(), end.getY()));
				points.add(new Point2D.Double(endBounds.getMaxX(), end.getY()));
				points.add(new Point2D.Double(endBounds.getMaxX(), maxY));
				points.add(new Point2D.Double(startBounds.getMaxX(), maxY));
			}

			PolygonRegion poly = new PolygonRegion(canvas.getSpread(),this.points);
			poly.setTemporary(this.isTemporary());
			canvas.getSpread().addRegion(poly);

			DefaultState.getInstance().setMode(DefaultDrawingMode.getInstance());
			new ImageToolBarSetModeByMouseEvent().setMode(e,ImageToolBar.modeNone);
		}
		setCursor(canvas);
	}

	private int checkLineIndex(Point2D point, SpreadCanvas canvas) {
		List<LineSegment> lines = canvas.getCurrentSpread().getLines();
		for(int i = 0; i < lines.size(); i++){
			if(lines.get(i).getView().contains(point)){
				return i;
			}
		}
		return -1;
	}

	@Override
	public void mouseClicked(GSMouseEvent e,SpreadCanvas canvas) {
		if(startcomponent != null && (e.getComponent() != startcomponent)){
			return ;
		}
		if (e.getButton() != MouseEvent.BUTTON1) {
			setCursor(canvas);
			return;
		}

	}
	@Override
	public void mouseMoved(GSMouseEvent e,SpreadCanvas canvas) {
		if (e.getComponent() == startcomponent) {
			out = false;
		} else {
			out = true;
		}
		setCursor(canvas);
		if(startcomponent != null && (e.getComponent() != startcomponent)){
			return ;
		}
		super.mouseMoved(e,canvas);
		setCursor(canvas);
	}

	@Override
	public void paint(Graphics g,SpreadCanvas canvas) {
		if (out) return;
		super.paint(g,canvas);
		double ratio = canvas.getImageLabel().getRatio();
		double gapX = canvas.getImageLabel().getGapWidth();
		double gapY = canvas.getImageLabel().getGapHeight();
		if (start != null) {
			drawCirclePoint(new Point2D.Double(start.getX()*ratio+gapX, start.getY()*ratio+gapY), g);
		}
		if (end != null) {
			drawCirclePoint(new Point2D.Double(end.getX()*ratio+gapX, end.getY()*ratio+gapY), g);
		}

		setCursor(canvas);
	}

	private void drawCirclePoint(Point2D point, Graphics g) {
		Ellipse2D.Double circle = new Ellipse2D.Double(point.getX() - 5, point.getY() - 5, 10, 10);
		Color oldColor = g.getColor();
		g.setColor(Color.BLUE);
		((Graphics2D) g).fill(circle);
		g.setColor(oldColor);
	}

	@Override
	public void cancel() {
		this.points = new ArrayList<Point2D>();
		this.start = null;
		this.end = null;
		return;
	}
}
