package smart_gs.smleditor.swingui;

import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import javax.swing.JFrame;


import smart_gs.drawing_tool.LineSegEditorCanvas;
import smart_gs.drawing_tool.LineSegEditorImageLabel;
import smart_gs.drawing_tool.drawing_mode.LineSegEditorDrawMode;
import smart_gs.drawing_tool.drawing_mode.LineSegEditorMode;
import smart_gs.drawing_tool.state.LineSegEditorState;
import smart_gs.logical.LineSegmentForEdit;
import smart_gs.logical.Spread;
import smart_gs.swingui.WorkspaceWindow;
import smart_gs.swingui.toolbar.LineSegEditorToolBar;
import smart_gs.swingui.toolbar.action.LineSegEditorTiltImagePanel;
import smart_gs.util.GSLog;
import smart_gs.util.Pair;
import sml_editor.logical.LineDirection;


@SuppressWarnings("serial")
public class LineSegEditor extends JFrame {
	
	private LineSegEditorCanvas canvas;
	private LineSegEditorImageLabel imageLabel;
	private Spread spread;
	private LineSegEditorToolBar toolBar;
	private Stack<List<LineSegmentForEdit>> lineStack = new Stack<List<LineSegmentForEdit>>();
	private Stack<Double> degreeStack = new Stack<Double>();
	private LineSegEditorTiltImagePanel tiltPanel;
	private GSLog log = GSLog.getInstance();
	
	public void clearUndoStack() {
		clearDegreeStack();
		clearLineStack();
	}
	
	public boolean undoStackIsEmpty() {
		return lineStackIsEmpty() && degreeStackIsEmpty();
	}
	
	public Pair<Double, List<LineSegmentForEdit>> popUndoStack() {
		System.out.println("Degree Stack Pre: " + degreeStack);
		System.out.println("Line Stack Pre: " + lineStack);
		Double degree = popDegreeStack();
		List<LineSegmentForEdit> lines = popLineStack();
		System.out.println("Degree Stack: " + degreeStack);
		System.out.println("Line Stack: " + lineStack);
		return new Pair<Double, List<LineSegmentForEdit>>(degree,lines);
	}
	
	public void pushUndoStack() {
		pushDegreeStack();
		pushLineStack();
	}
	
	public Pair<Double, List<LineSegmentForEdit>> peekUndoStack() {
		return new Pair<Double, List<LineSegmentForEdit>>(peekDegreeStack(),peekLineStack());
	}

	public void pushUndoStack(Double degree, List<LineSegmentForEdit> lines) {
		pushDegreeStack(degree);
		pushLineStack(lines);
	}
	
	public void clearDegreeStack() {
		degreeStack = new Stack<Double>();
	}
	
	public boolean degreeStackIsEmpty() {
		return degreeStack.empty();
	}
	
	public Double popDegreeStack() {
		if (! degreeStack.empty()) {
			double degree = degreeStack.pop();
			tiltPanel.setDegree(degree);
			this.tiltImageForPop();
			return degree;
		}
		else 
			return null;
	}
	
	public void pushDegreeStack() {
		pushDegreeStack(this.tiltPanel.getDegree());
	}

	public void pushDegreeStack(Double degree) {
		this.degreeStack.push(new Double(degree));
	}

	public Double peekDegreeStack() {
		return degreeStack.peek();
	}

	public void clearLineStack() {
		lineStack = new Stack<List<LineSegmentForEdit>>();
	}
	
	public boolean lineStackIsEmpty() {
		return lineStack.empty();
	}
	
	public List<LineSegmentForEdit> popLineStack() {
		if (! lineStack.empty()) {
			List<LineSegmentForEdit> toplines = lineStack.pop();
			this.setLinesForEdit(toplines);
			return toplines;
		}
		else 
			return null;
	}
	
	public void pushLineStack() {
		pushLineStack(this.getLinesForEdit());
	}

	public void pushLineStack(List<LineSegmentForEdit> lines) {
		this.lineStack.push(deepCopy(lines));
	}
	
	private List<LineSegmentForEdit> deepCopy(List<LineSegmentForEdit> lines) {
		List<LineSegmentForEdit> ans = new ArrayList<LineSegmentForEdit>();
		for (LineSegmentForEdit line : lines) {
			ans.add(line.deepCopy());
		}
		return ans;
	}

	public List<LineSegmentForEdit> peekLineStack() {
		return lineStack.peek();
	}

	public List<LineSegmentForEdit> getLinesForEdit() {
		return canvas.getLines();
	}
	
	public void setLineDirectionForEdit(LineDirection lineDirection) {
		this.spread.setLineDirection(lineDirection);
	}
	public void setLinesForEdit(List<LineSegmentForEdit> lines) {
		this.getLineSegEditorCanvas().setLinesForEdit(lines);
	}

	public void setSpread(Spread spread){
		this.spread = spread;
	}
	
	public Spread getSpread() {
		return this.spread;
	}

	public LineSegEditor(Spread spread)  {
		super("Line Segment Editor: " + spread.getName());
		this.canvas = new LineSegEditorCanvas(spread,this);
		this.imageLabel = this.canvas.getLineSegEditorImageLabel();

		this.spread = spread;
		this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		this.setLayout(new BorderLayout());
		
		toolBar = new LineSegEditorToolBar(this);
		this.tiltPanel = new LineSegEditorTiltImagePanel(this);

		this.getContentPane().add(tiltPanel,BorderLayout.NORTH);
		this.getContentPane().add(toolBar,BorderLayout.EAST);
		this.getContentPane().add(canvas,BorderLayout.CENTER);

		this.clearUndoStack();
		
		this.setSize(900,700);
		this.setVisible(true);
		this.repaint();
	}

	
	
	public void updateGUI() {
		validate();
		repaint();
	}

	public Frame getFrame() {
		return this;
	}

	public LineSegEditorCanvas getLineSegEditorCanvas() {
		return canvas;
	}
	
	public LineSegEditorImageLabel getLineSegEditorImageLabel() {
		return imageLabel;
	}
	
	public 	LineSegEditorToolBar getLineSegEditorToolBar(){
		return  this.toolBar;
	}
	
	public void setMode(LineSegEditorMode mode) {
		this.imageLabel.setMode(mode);
	}
	
	public LineSegEditorMode getMode() {
		return this.imageLabel.getMode();
	}
	
	//20110220 shimizu add
	public void changeLinesSize(double mag){
		double m = mag/100;
		List<LineSegmentForEdit> lines = this.getLinesForEdit();
		for(int i=0;i<lines.size();i++){
			lines.get(i).changeSize(m);	
		}
		WorkspaceWindow.getInstance().repaint();
	}
	
	//20110303 shimizu add
	public void slideLines(double x, double y){
		List<LineSegmentForEdit> lines = this.getLinesForEdit();
		for(int i=0;i<lines.size();i++){
			lines.get(i).slideLine(x, y);
		}
		WorkspaceWindow.getInstance().repaint();
	}

	public void rewriteLineSegIndexes() {
		this.getLineSegEditorCanvas().rewriteLineSegIndexes();
		
	}

	public LineSegEditorMode getLineSegEditorMode() {
		return this.imageLabel.getMode();
	}

	public void setLineSegEditorToDrawMode() {
		this.imageLabel.setMode(this.imageLabel.getDrawMode());
	}

	public LineSegEditorMode getLineSegDefaultMode() {
		// TODO Auto-generated method stub
		return null;
	}

	public LineSegEditorState getLineSegEditorState() {
		// TODO Auto-generated method stub
		return null;
	}

	public LineSegEditorDrawMode getLineSegEditorDrawMode() {
		// TODO Auto-generated method stub
		return this.getDrawMode();
	}

	public LineSegEditorDrawMode getDrawMode() {
		return this.imageLabel.getDrawMode();
	}

	public int selectedIndex() {
		return imageLabel.selectedIndex();
	}

	public LineSegEditorTiltImagePanel getTiltPanel() {
		return this.tiltPanel;
	}
	
//	This tilts and sets the REAL lines and the original image.
//	Not current ones!!
	public void tilt() {
		Image image = this.spread.getImage();

		int w = image.getWidth(null);
		int h = image.getHeight(null);
		int type = BufferedImage.TYPE_INT_RGB;  // other options, see api

		double degree = this.tiltPanel.getDegree();
		double degreeAbs = Math.abs(degree);
		int new_w = (int)Math.ceil(w*Math.cos(degreeAbs)+h*Math.sin(degreeAbs));
		int new_h = (int)Math.ceil(w*Math.sin(degreeAbs)+h*Math.cos(degreeAbs));
		BufferedImage bimage = new BufferedImage(new_w,new_h,type);
		Graphics2D g2 = bimage.createGraphics();

		AffineTransform atForTilt = createAffinetransformForTilt();
		g2.drawImage(image, atForTilt, null);
		g2.dispose();
		
		List<LineSegmentForEdit> tiltedLines = getTiltedLinesForEdit(atForTilt,degree);
		this.getContentPane().remove(this.canvas);
		this.canvas = new LineSegEditorCanvas(this.spread,bimage,this,tiltedLines);
		this.canvas.setVisible(true);
		this.getContentPane().add(this.canvas,BorderLayout.CENTER);
		this.imageLabel = this.canvas.getLineSegEditorImageLabel();
		rewriteLineSegIndexes();
		this.invalidate();
		this.validate();
		this.repaint();
	}

//	This tilts and sets the original image.
//	Not current one!!
	public void tiltImageForPop() {
		Image image = this.spread.getImage();

		int w = image.getWidth(null);
		int h = image.getHeight(null);
		int type = BufferedImage.TYPE_INT_RGB;  // other options, see api

		double degree = this.tiltPanel.getDegree();
		double degreeAbs = Math.abs(degree);
		int new_w = (int)Math.ceil(w*Math.cos(degreeAbs)+h*Math.sin(degreeAbs));
		int new_h = (int)Math.ceil(w*Math.sin(degreeAbs)+h*Math.cos(degreeAbs));
		BufferedImage bimage = new BufferedImage(new_w,new_h,type);
		Graphics2D g2 = bimage.createGraphics();

		AffineTransform atForTilt = createAffinetransformForTilt();
		g2.drawImage(image, atForTilt, null);
		g2.dispose();
		
		List<LineSegmentForEdit> lines = this.canvas.getLines();
		this.getContentPane().remove(this.canvas);
		this.canvas = new LineSegEditorCanvas(this.spread,bimage,this,lines);
		this.canvas.setVisible(true);
		this.getContentPane().add(this.canvas,BorderLayout.CENTER);
		this.imageLabel = this.canvas.getLineSegEditorImageLabel();
	}
	
	public AffineTransform createAffinetransformForTilt() {
		double h = this.spread.getImage().getWidth(null);
		double degree = this.tiltPanel.getDegree();
		AffineTransform transformation = new AffineTransform();
		transformation.translate(h*Math.sin(-degree),0);
		transformation.rotate(-degree);
		return transformation;
	}

	private List<LineSegmentForEdit> getTiltedLinesForEdit(AffineTransform atForTilt, double degree) {
		List<LineSegmentForEdit> ans = new ArrayList<LineSegmentForEdit>();

		for (LineSegmentForEdit line : getLinesForEdit()) {
			ans.add(new LineSegmentForEdit(this, line, line.getLineDirection()));
		}
		return ans;
	}
}
