package smart_gs.drawing_tool.drawing_mode;

import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JOptionPane;

import smart_gs.drawing_tool.ExLine2D;
import smart_gs.drawing_tool.LineSegEditorCanvas;
import smart_gs.drawing_tool.state.DefaultState;
import smart_gs.drawing_tool.view.LineView;
import smart_gs.drawing_tool.view.View;
import smart_gs.logical.LineSegmentForEdit;
import smart_gs.logical.Spread;
import smart_gs.smleditor.swingui.LineSegEditor;
import smart_gs.swingui.GSMouseEvent;
import smart_gs.util.GSLog;
import smart_gs.util.Intersection2D;
import sml_editor.logical.LineDirection;

public class LineSegEditorDivideMode implements LineSegEditorMode{
	
	private Point2D start=null;
	private Point2D end=null;
	
	private ExLine2D cutLine;
	private List<LineSegmentForEdit> selectedLineSingletonList;
	private ArrayList<LineSegmentForEdit> workLines;
	private boolean in_divide_mode_loop = false;
	private int selectedLineSegIndex;
	private LineSegEditor editor;
	private ArrayList<Integer> selectedLineSegIndexes;
	private View view;
	private Integer original_lines_size = 0;
	
	public LineSegEditor getEditor() {
		return this.editor;
	}
	
	public LineSegEditorDivideMode(LineSegEditor editor, ArrayList<Integer> selectedIndexes0) {
		this.editor = editor;

		if (selectedIndexes0 instanceof ArrayList) {
			this.workLines = (ArrayList<LineSegmentForEdit>) this.editor.getLinesForEdit();
		} else {
			this.workLines = new ArrayList<LineSegmentForEdit>(this.editor.getLinesForEdit());
		}
		this.selectedLineSegIndexes = new ArrayList<Integer>();
		Collections.sort(selectedIndexes0);
		this.original_lines_size = this.workLines.size();
		for (int j = 0; j < selectedIndexes0.size(); j++) {
			this.selectedLineSegIndexes.add(original_lines_size-1-selectedIndexes0.get(j));
		}
		this.workLines = new ArrayList<LineSegmentForEdit>(this.editor.getLinesForEdit());
		Collections.reverse(this.workLines);

		this.editor.pushUndoStack();
		
		set_in_divide_mode_loop();
		LineSegEditorDivideMode_LOOP();
	}
	
	public boolean is_in_divide_mode_loop() {return this.in_divide_mode_loop;}

	public void set_in_divide_mode_loop() {this.in_divide_mode_loop = true;}
	
	public void reset_in_divide_mode_loop() {this.in_divide_mode_loop = false;}
	
	public void LineSegEditorDivideMode_LOOP() {
		this.selectedLineSegIndex = this.selectedLineSegIndexes.get(0);
		this.selectedLineSegIndexes.remove(0);
		LineSegmentForEdit selectedLineSeg = workLines.get(selectedLineSegIndex);
		this.view = selectedLineSeg.getView();
		view.setIsSelected(true);
		this.selectedLineSingletonList = new ArrayList<LineSegmentForEdit>();
		this.selectedLineSingletonList.add(selectedLineSeg);
		this.editor.setLinesForEdit(this.selectedLineSingletonList);
		this.editor.getLineSegEditorCanvas().getLineSegEditorImageLabel().setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));	
		this.editor.repaint();
	}
	
	
	public void mousePressed(GSMouseEvent e, LineSegEditorCanvas canvas) {
		setCursor(canvas);
		if (e.getButton() != MouseEvent.BUTTON1) {
			this.cancel();
			return;
		}
		if(this.start == null){
			this.start = e.getPoint();
			return;
		}
		this.end = e.getPoint();
	}
	
	public void mouseClicked(GSMouseEvent e,LineSegEditorCanvas canvas) {
		if (e.getButton() != MouseEvent.BUTTON1) {
			this.cancel();
			return;
		}
		setCursor(canvas);
		
		if(e.getClickCount() == 2){
			cutLine = new ExLine2D(start,end);
			this.start = null;
			this.end = null;
			LineSegEditorDivideModePostProcess(cutLine,canvas);
			this.editor.setDefaultMode();
		}
	}
	
	private void LineSegEditorDivideModePostProcess(ExLine2D cutLine,
			LineSegEditorCanvas canvas) {
		Point2D firstDividePoint=null;
		int firstDivideSegmentIndex = 0;
		Point2D secondDividePoint=null;
		int secondDivideSegmentIndex = 0;
		List<Point2D> points = selectedLineSingletonList.get(0).getPoints();
		int size = points.size();
		
		for (int i=0;i<size;i++){
			Line2D tmpLine = new Line2D.Double(points.get(i), points.get((i+1)%size));
			boolean intsects =	this.cutLine.intersectsLine(tmpLine);
			if (intsects) {
				if (firstDividePoint==null) {
					firstDivideSegmentIndex = i;
					firstDividePoint = Intersection2D.getIntersection(cutLine,tmpLine);
					if (firstDividePoint==null){
						JOptionPane.showMessageDialog(null,"Failed to compute the first crossing point for " + i + "-th segment. Try again!");
						return;
					}
				} else if (secondDividePoint==null){
					secondDivideSegmentIndex = i;
					secondDividePoint = Intersection2D.getIntersection(cutLine,tmpLine);
					if (secondDividePoint==null){
						JOptionPane.showMessageDialog(null,"Failed to compute the second crossing point for " + i + "-th segment. Try again!");
						return;
					}
				} else {
					JOptionPane.showMessageDialog(null,"Too many crossings. Try again!");
					return;
				}
			}
		}
		
		if (firstDividePoint == null || secondDividePoint == null ) {
			GSLog.getInstance().info("Reset");
			return;
		}
		
		
		List<Point2D> firstPoints = new ArrayList<Point2D>();
		List<Point2D> secondPoints = new ArrayList<Point2D>();
		for (int i=0;i<=firstDivideSegmentIndex;i++) {
			firstPoints.add(points.get(i));
		}
		firstPoints.add(firstDividePoint);
		firstPoints.add(secondDividePoint);
		for (int i=secondDivideSegmentIndex+1;i<size;i++) {
			firstPoints.add(points.get(i));
		}
		secondPoints.add(firstDividePoint);
		for (int i=firstDivideSegmentIndex+1;i<=secondDivideSegmentIndex;i++) {
			secondPoints.add(points.get(i));
		}
		secondPoints.add(secondDividePoint);
		
		int firstPointsSize = firstPoints.size();
		int secondPointsSize = secondPoints.size();
		
		if (firstPointsSize == 0 || secondPointsSize == 0) {
			JOptionPane.showMessageDialog(null,"Illegal cut line. Try again!");
			return;
		}

		workLines.remove(selectedLineSegIndex);
		Spread spread = editor.getSpread();
		
		if (spread.getLineDirection() != LineDirection.VERTICAL)
		{
			Point2D firstPointsLeftest = firstPoints.get(0);
			Point2D secondPointsLeftest  = secondPoints.get(0);
			for (int i=1; i<firstPointsSize; i++) {
				Point2D cp = firstPoints.get(i);
				if (firstPointsLeftest.getX() > cp.getX()) {
					firstPointsLeftest = cp;
				}
			}
			for (int i=1; i<secondPointsSize; i++) {
				Point2D cp = secondPoints.get(i);
				if (secondPointsLeftest.getX() > cp.getX()) {
					secondPointsLeftest = cp;
				}
			}
			if (firstPointsLeftest.getX() <= secondPointsLeftest.getX()) {
				workLines.add(selectedLineSegIndex,createLineSegmentForEdit(firstPoints,this.editor.getSpread().getLineDirection()));	
				workLines.add(selectedLineSegIndex+1,createLineSegmentForEdit(secondPoints,this.editor.getSpread().getLineDirection()));
			} else {
				workLines.add(selectedLineSegIndex,createLineSegmentForEdit(secondPoints,this.editor.getSpread().getLineDirection()));
				workLines.add(selectedLineSegIndex+1,createLineSegmentForEdit(firstPoints,this.editor.getSpread().getLineDirection()));	
			}
		} else {
			Point2D firstPointsTopmost = firstPoints.get(0);
			Point2D secondPointsTopmost  = secondPoints.get(0);
			for (int i=1; i<firstPointsSize; i++) {
				Point2D cp = firstPoints.get(i);
				if (firstPointsTopmost.getY() > cp.getY()) {
					firstPointsTopmost = cp;
				}
			}
			for (int i=1; i<secondPointsSize; i++) {
				Point2D cp = secondPoints.get(i);
				if (secondPointsTopmost.getY() > cp.getY()) {
					secondPointsTopmost = cp;
				}
			}
			if (firstPointsTopmost.getY() <= secondPointsTopmost.getY()) {
				workLines.add(selectedLineSegIndex,createLineSegmentForEdit(firstPoints,this.editor.getSpread().getLineDirection()));	
				workLines.add(selectedLineSegIndex+1,createLineSegmentForEdit(secondPoints,this.editor.getSpread().getLineDirection()));
			} else {
				workLines.add(selectedLineSegIndex,createLineSegmentForEdit(secondPoints,this.editor.getSpread().getLineDirection()));
				workLines.add(selectedLineSegIndex+1,createLineSegmentForEdit(firstPoints,this.editor.getSpread().getLineDirection()));	
			}
		}
		this.editor.setLinesForEdit(workLines);
		
		if (this.selectedLineSegIndexes.size() == 0) {
			this.reset_in_divide_mode_loop();
			Collections.reverse(workLines);
			this.editor.setLinesForEdit(workLines);
			this.editor.setMode(new LineSegEditorSelectMode(this.editor));
			this.editor.rewriteLineSegIndexes();
			this.editor.repaint();
		} else {
			LineSegEditorDivideMode_LOOP();
		}
		
	}
	
	private LineSegmentForEdit createLineSegmentForEdit(List<Point2D> firstPoints, LineDirection lineDirection) {
		return new LineSegmentForEdit(this.editor, firstPoints, lineDirection);
	}

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

	@Override
	public void paint(Graphics g,LineSegEditorCanvas canvas) {	
		if(this.start == null || this.end == null){
			return ;
		}
		double ratio = canvas.getLineSegEditorImageLabel().getRatio();
		double gapX = canvas.getLineSegEditorImageLabel().getGapWidth();
		double gapY = canvas.getLineSegEditorImageLabel().getGapHeight();

		new LineView(new ExLine2D(start,end)).enlargedView(ratio,gapX,gapY).draw((Graphics2D)g);

		setCursor(canvas);
	}
	
	public void cancel() {
		this.editor.undo();
		this.editor.setDefaultMode();
	}
	
	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));		
	}

	@Override
	public LineSegEditor getParentLinesegEditor() {
		// TODO Auto-generated method stub
		return null;
	}
	@Override
	public void setParentLinesegEditor() {
		// TODO Auto-generated method stub
		
	}

}
