package smart_gs.smleditor.swingui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;


import smart_gs.drawing_tool.LineSegEditorCanvas;
import smart_gs.drawing_tool.LineSegEditorImageLabel;
import smart_gs.drawing_tool.drawing_mode.LineSegEditorDeleterMode;
import smart_gs.drawing_tool.drawing_mode.LineSegEditorDivideMode;
import smart_gs.drawing_tool.drawing_mode.LineSegEditorDrawPolygonMode;
import smart_gs.drawing_tool.drawing_mode.LineSegEditorDrawRectangleMode;
import smart_gs.drawing_tool.drawing_mode.LineSegEditorEditVertexMode;
import smart_gs.drawing_tool.drawing_mode.LineSegEditorMode;
import smart_gs.drawing_tool.drawing_mode.LineSegEditorSelectMode;
import smart_gs.drawing_tool.drawing_mode.LineSegEditorSemiAutomaticMode;
import smart_gs.drawing_tool.state.LineSegEditorState;
import smart_gs.image_search.logical.TextType;
import smart_gs.logical.LineSegmentForEdit;
import smart_gs.logical.Preference;
import smart_gs.logical.Spread;
import smart_gs.swingui.WorkspaceTabbedPane;
import smart_gs.swingui.WorkspaceWindow;
import smart_gs.swingui.toolbar.HistoryToolBar;
import smart_gs.swingui.toolbar.action.LineSegDscInformationAction;
import smart_gs.swingui.toolbar.action.LineSegEditorChangeIndexesActionListener;
import smart_gs.swingui.toolbar.action.LineSegEditorDeleteActionListener;
import smart_gs.swingui.toolbar.action.LineSegEditorDivideActionListener;
import smart_gs.swingui.toolbar.action.LineSegEditorDrawPolygonActionListener;
import smart_gs.swingui.toolbar.action.LineSegEditorDrawRectangleActionListener;
import smart_gs.swingui.toolbar.action.LineSegEditorEditVertexActionListener;
import smart_gs.swingui.toolbar.action.LineSegEditorMoveAndResizeActionListener;
import smart_gs.swingui.toolbar.action.LineSegEditorRotateActionListener;
import smart_gs.swingui.toolbar.action.LineSegEditorSelectActionListener;
import smart_gs.swingui.toolbar.action.LineSegEditorSemiAutomaticLineSegmentationActionListener;
import smart_gs.swingui.toolbar.action.LineSegEditorTiltImagePanel;
import smart_gs.swingui.toolbar.action.LineSegEditorUndoRedoActionListener;
import smart_gs.swingui.toolbar.action.LineSegEditorZoomInOutActionListener;
import smart_gs.swingui.toolbar.action.SaveLineSegmentsInSegfoFormatAction;
import smart_gs.swingui.toolbar.action.SaveLineSegmentsInXMLFormatAction;
import smart_gs.swingui.toolbar.action.SegfoFileFilter;
import smart_gs.swingui.toolbar.action.XmlFileFilter;
import smart_gs.util.DSCFileMaker;
import smart_gs.util.GSLog;
import smart_gs.util.Pair;
import sml_editor.logical.LineDirection;


@SuppressWarnings("serial")
public class LineSegEditor extends JFrame implements WindowListener {
	
	public static final ActionListener lsgEditor_changeindex = null;
	private LineSegEditorCanvas canvas;
	private LineSegEditorImageLabel imageLabel;
	private Spread spread;
	private Stack<List<LineSegmentForEdit>> lineStack = new Stack<List<LineSegmentForEdit>>();
	private Stack<Double> degreeStack = new Stack<Double>();
	private Stack<Pair<Double,List<LineSegmentForEdit>>> redoStack = new Stack<Pair<Double,List<LineSegmentForEdit>>>();
	private Stack<Double> degreeRedoStack = new Stack<Double>();
	private LineSegEditorTiltImagePanel tiltPanel;
	private int resize_magnification = 1;
	private LineSegEditorNorthPanel lineSegEditorNorthPanel;
	private LineSegEditorToolBar lineSegEditorToolBar;
	private boolean closingOrSavingCanceled = false;
	private boolean changed = false;
	
	public static LineDirection line_direction = LineDirection.HORIZONTAL;
	
	public final ActionListener lsgEditor_editVertex;
	public final ActionListener lsgEditor_select;
	public final ActionListener lsgEditor_drawPolygon;
	public final ActionListener lsgEditor_drawRectangle;
	public final ActionListener lsgEditor_autoDrawRectangles;
	public final ActionListener lsgEditor_divide;
	public final ActionListener lsgEditor_changeIndex;
	public final ActionListener lsgEditor_delete;
	public final ActionListener lsgEditor_deleteNarrowerLines;
	public final ActionListener lsgEditor_undo;
	public final ActionListener lsgEditor_redo;
	public final ActionListener lsgEditor_zoomIn;
	public final ActionListener lsgEditor_zoomOut;
	public final ActionListener lsgEditor_moveAndResize;
	public final ActionListener lsgEditor_rotate;
	public final ActionListener lsgEditor_info;

	
	public LineSegEditor(Spread spread)  {
		
		this.lsgEditor_editVertex            = new LineSegEditorEditVertexActionListener(this);
		this.lsgEditor_select                = new LineSegEditorSelectActionListener(this);
		this.lsgEditor_drawPolygon           = new LineSegEditorDrawPolygonActionListener(this);
		this.lsgEditor_drawRectangle         = new LineSegEditorDrawRectangleActionListener(this);
		this.lsgEditor_autoDrawRectangles    = new LineSegEditorSemiAutomaticLineSegmentationActionListener(this);
		this.lsgEditor_divide                = new LineSegEditorDivideActionListener(this);
		this.lsgEditor_changeIndex           = new LineSegEditorChangeIndexesActionListener(this);
		this.lsgEditor_delete                = new LineSegEditorDeleteActionListener(this,0);
		this.lsgEditor_deleteNarrowerLines   = new LineSegEditorDeleteActionListener(this,1);
		this.lsgEditor_undo                  = new LineSegEditorUndoRedoActionListener(this,0);
		this.lsgEditor_redo                  = new LineSegEditorUndoRedoActionListener(this,1);
		this.lsgEditor_zoomIn                = new LineSegEditorZoomInOutActionListener(this,"ZoomIn");
		this.lsgEditor_zoomOut               = new LineSegEditorZoomInOutActionListener(this,"ZoomOut");
		this.lsgEditor_moveAndResize         = new LineSegEditorMoveAndResizeActionListener(this);
		this.lsgEditor_rotate                = new LineSegEditorRotateActionListener(this);
		this.lsgEditor_info                  = new LineSegDscInformationAction(this);
		                                       
		
		this.spread = spread;
		this.canvas = new LineSegEditorCanvas(this.spread,this);
		this.canvas.setPosition(Preference.getInstance().getImagePosition());
		this.imageLabel = this.canvas.getLineSegEditorImageLabel();
		// Remove this code later, when LineSegEditorImageLabel is refactored so that
		// it does not use JOptionPane to make a line segment file.
		if (this.imageLabel.isCanceled()) {
			return;
		}
			
		this.setTitle("Line Segment Editor: " + this.spread.getName() + " (" + this.imageLabel.getLineSegFileType() + ")");
		
		
		this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		this.addWindowListener(this);
		this.setLayout(new BorderLayout());
		
//		this.spreadTreePanelForLineSegEditor = new SpreadTreePanelForLineSegEditor(this);
		
		this.tiltPanel = new LineSegEditorTiltImagePanel(this);
		this.lineSegEditorNorthPanel = new LineSegEditorNorthPanel(this.tiltPanel,this);
		this.lineSegEditorToolBar = lineSegEditorNorthPanel.getToolBar();
		this.lineSegEditorNorthPanel.setLineDirectionIcon(this.line_direction);

//		this.getContentPane().add(spreadTreePanelForLineSegEditor,BorderLayout.WEST);
		this.getContentPane().add(lineSegEditorNorthPanel,BorderLayout.NORTH);
		this.getContentPane().add(canvas,BorderLayout.CENTER);

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

	public void clearUndoStack() {
		clearDegreeStack();
		clearLineStack();
	}
	
	public void clearUndoRedoStack() {
		clearRedoStack();
		clearDegreeStack();
		clearLineStack();
	}
	
	public boolean isUndoStackEmpty() {
		if (lineStackIsEmpty() & degreeStackIsEmpty()) {
			return true;
		}
		
		if (! lineStackIsEmpty() & degreeStackIsEmpty()) {
			GSLog.getInstance().warn("line stack is non-empty and degree stack is empty");
		}
		
		if (lineStackIsEmpty() & ! degreeStackIsEmpty()) {
			GSLog.getInstance().warn("line stack is empty and degree stack is non-empty");
		}
		
		return false;
	}
	
	
	public void undo() {
		if (! isUndoStackEmpty()) {
			pushRedoStack(new Pair<Double,List<LineSegmentForEdit>>(this.tiltPanel.getDegree(),this.getLinesForEdit()));
			popUndoStack();
		}
	}
	
	public void redo() {
		if (! redoStackIsEmpty()) {
			pushUndoStackKeepingRedoStack();
			Pair<Double, List<LineSegmentForEdit>> pair = popRedoStack();
			this.tiltPanel.setDegree(pair.getLeft());
			this.setLinesForEdit(pair.getRight());
			this.repaint();
		}
	}
	
	private Pair<Double, List<LineSegmentForEdit>> popRedoStack() {
		return this.redoStack.pop();
	}

	private void pushRedoStack (Pair<Double, List<LineSegmentForEdit>> pair){
		this.redoStack.push(pair);
	}

	public Pair<Double, List<LineSegmentForEdit>> popUndoStack() {
		Double degree = popDegreeStack();
		List<LineSegmentForEdit> lines = popLineStack();
		return new Pair<Double, List<LineSegmentForEdit>>(degree,lines);
	}
	
	public void pushUndoStack() {
		setChanged();
		pushDegreeStack();
		pushLineStack();
		clearRedoStack();
	}
	
	public void pushUndoStackKeepingRedoStack() {
		setChanged();
		pushDegreeStack();
		pushLineStack();
	}
	
	private void clearRedoStack() {
		this.redoStack = new Stack<Pair<Double,List<LineSegmentForEdit>>>();		
	}

	public Pair<Double, List<LineSegmentForEdit>> peekUndoStack() {
		return new Pair<Double, List<LineSegmentForEdit>>(peekDegreeStack(),peekLineStack());
	}

	public void pushUndoStack(Double degree, List<LineSegmentForEdit> lines) {
		setChanged();
		pushDegreeStack(degree);
		pushLineStack(lines);
	}
	
	public void clearDegreeStack() {
		this.degreeStack = new Stack<Double>();
		degreeRedoStack = new Stack<Double>();
	}
	
	public boolean degreeStackIsEmpty() {
		return this.degreeStack.empty();
	}
	
	public boolean degreeRedoStackIsEmpty() {
		return this.degreeRedoStack.empty();
	}
	
	public Double popDegreeStack() {
		if (! this.degreeStack.empty()) {
			double degree = this.degreeStack.pop();
			this.degreeRedoStack.push(degree);
			this.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 this.degreeStack.peek();
	}

	public void clearLineStack() {
		lineStack = new Stack<List<LineSegmentForEdit>>();
		redoStack = new Stack<Pair<Double,List<LineSegmentForEdit>>>();
	}
	
	public boolean lineStackIsEmpty() {
		return lineStack.empty();
	}
	
	public boolean redoStackIsEmpty() {
		return redoStack.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 Spread getSpread() {
		return this.spread;
	}

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

	public Frame getFrame() {
		return this;
	}

	public LineSegEditorCanvas getLineSegEditorCanvas() {
		return canvas;
	}
	
	public LineSegEditorImageLabel getLineSegEditorImageLabel() {
		return imageLabel;
	}
	
	public void setMode(LineSegEditorMode mode) {
		if (this.getMode() instanceof LineSegEditorDivideMode) {
			LineSegEditorDivideMode tmp = (LineSegEditorDivideMode)this.getMode();
			if (tmp.is_in_divide_mode_loop()) {
				this.undo();
				tmp.reset_in_divide_mode_loop();
			}
		}
		this.imageLabel.setMode(mode);
		lineSegEditorToolBar.resetButtons();
		if (mode instanceof LineSegEditorSemiAutomaticMode) {
			lineSegEditorToolBar.disableAutoButton();
		} else if (mode instanceof LineSegEditorDivideMode) {
			lineSegEditorToolBar.disableDivideButton();
		} else if (mode instanceof LineSegEditorDivideMode) {
			lineSegEditorToolBar.disableVertexedButton();
		} else if (mode instanceof LineSegEditorEditVertexMode) {
			lineSegEditorToolBar.disableVertexedButton();
		} else if (mode instanceof LineSegEditorDrawRectangleMode) {
			lineSegEditorToolBar.disableRectButton();
		} else if (mode instanceof LineSegEditorDrawPolygonMode) {
			lineSegEditorToolBar.disablePolygonButton();
		} else if (mode instanceof LineSegEditorDrawPolygonMode) {
			lineSegEditorToolBar.disableChangeIndexesButton();
		} else if (mode instanceof LineSegEditorDeleterMode) {
			lineSegEditorToolBar.disableDeleteButton();
		} 

	}
	
	public void setDefaultMode() {
		this.imageLabel.setMode(new LineSegEditorSelectMode(this));
		setLineSegEditorImageLabelCursor(new Cursor(Cursor.DEFAULT_CURSOR));
	}
	
	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 rotateLines(AffineTransform transformation) {
		List<LineSegmentForEdit> lines = this.getLinesForEdit();
		for(int i=0;i<lines.size();i++){
			lines.get(i).rotateLine(transformation);
		}		
	}

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

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

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

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

	public int getSelectedLineIndex() {
		return imageLabel.getSelectedLineIndex();
	}
	
	public ArrayList<Integer> getSelectedLineIndexes() {
		return imageLabel.getSelectedLineIndexes();
	}

	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();
		this.getContentPane().remove(this.canvas);
		this.canvas.resetLineSegEditorCanvas(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.resetLineSegEditorCanvas(this.spread,bimage,this,lines);
		this.canvas.setVisible(true);
		this.getContentPane().add(this.canvas,BorderLayout.CENTER);
		this.imageLabel = this.canvas.getLineSegEditorImageLabel();
	}
	
	public AffineTransform createAffinetransformForTilt() {
		return createAffinetransformForTilt(this.tiltPanel.getDegree());
	}
	
	public AffineTransform createAffinetransformForTilt(double degree) {
		double h = this.spread.getImage().getWidth(null);
		AffineTransform transformation = new AffineTransform();
		transformation.translate(h*Math.sin(-degree),0);
		transformation.rotate(-degree);
		return transformation;
	}

	private List<LineSegmentForEdit> getTiltedLinesForEdit() {
		List<LineSegmentForEdit> ans = new ArrayList<LineSegmentForEdit>();

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

	public int getResizeMagnificaiton() {
		return this.resize_magnification ;
	}
	
	public void setResizeMagnificaiton(int mag) {
		this.resize_magnification = mag;
	}

	public int getScale() {
		return this.imageLabel.scale;
	}

	public LineSegEditorCanvas getSpreadCanvas() {
		return this.canvas;
	}

	public void setLineSegEditorImageLabelCursor(Cursor cursor) {
		this.imageLabel.setCursor(cursor);
		
	}

	@Override
	public void windowActivated(WindowEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void windowClosed(WindowEvent arg0) {
	}

	@Override
	public void windowClosing(WindowEvent arg0) {windowClosingOperation();}
	
	public void windowClosingOperation(){
		String selectvalues[] = {"Save (XML)", "Save (Segfo)", "Close without saving", "Cancel closing"};
		if (this.changed) {
			int selection = JOptionPane.showOptionDialog(this,
					"Choose Options:	", 
					"Changes not saved", 
					0, 
					JOptionPane.QUESTION_MESSAGE,
					null, 
					selectvalues, 
					selectvalues[0]);
			switch (selection) {
			   case 0: this.closingOrSavingCanceled = saveInXMLForced();
			   		   WorkspaceWindow.getInstance().updateAndPaintSpreadTree();
			           break;
			   case 1: this.closingOrSavingCanceled = saveInSegfo(true);
			           WorkspaceWindow.getInstance().updateAndPaintSpreadTree();
	                   break;
			   case 2: this.closingOrSavingCanceled = false;
	                   break;
			   default: this.closingOrSavingCanceled = true;
				       break;			
			}

		}
		if (this.closingOrSavingCanceled) return;
		WorkspaceWindow.getInstance().initializeLineSegEditor();		
		this.dispose();
	}

	@Override
	public void windowDeactivated(WindowEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void windowDeiconified(WindowEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void windowIconified(WindowEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void windowOpened(WindowEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	
	public boolean saveInXML () {	
		return saveInXML(false);
	}
	
	public boolean saveInXMLForced () {	
		return saveInXML(true);
	}
	
	// return value == true means "canceled"
	public boolean saveInXML (boolean forced) {	
		JFileChooser chooser;
		Spread spread = this.getSpread();

		String folderpath = Preference.getInstance().getImageFolderPathString() + spread.getFolderPath();
		String defaultFilePathInString = folderpath + spread.getFileNameWithoutExtension() + ".xml";
		try {
			defaultFilePathInString = new File(defaultFilePathInString).getCanonicalPath();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		chooser = new JFileChooser(folderpath);
		chooser.setSelectedFile(new File(defaultFilePathInString));	
		chooser.setMultiSelectionEnabled(false);
		chooser.setFileFilter(new XmlFileFilter());
		chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);

		spread = this.getSpread();
		List<LineSegmentForEdit> segments = this.getLinesForEdit();
		if(segments == null){
			return false;
		}
		String folderPath = null;
		String filename = null;
		String filetype = null;
		String lineDirectionType = null;

		if (forced) {
			folderPath = Spread.DSC_FOLDER_PATH+spread.getSpreadDirParent().getPath();
			filename = spread.getFileNameWithoutExtension();
			filetype = spread.getFileNameExtension();
			if (spread.getLineDirection() == LineDirection.HORIZONTAL) {
				lineDirectionType = "HORIZONTAL";
			} else if (spread.getLineDirection() == LineDirection.VERTICAL) {
				lineDirectionType = "VERTICAL";
			} else {
				lineDirectionType = "UNDEFINED";
			}
			(new DSCFileMaker(spread)).saveLineSegmentsInXMLFormat(segments, folderPath, filename, filetype, lineDirectionType);
			resetChanged();
			return false;
		} else {
			String chosenPathString = null;
			File file = null;

			int input = chooser.showSaveDialog(WorkspaceWindow.getInstance());

			if (input == JFileChooser.CANCEL_OPTION) {
				return true;
			}

			file = chooser.getSelectedFile();

			try {
				chosenPathString = file.getCanonicalPath();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return true;
			}

			if (! chosenPathString.equals(defaultFilePathInString) && file.exists()) {
				int ans = JOptionPane.showConfirmDialog(this,String.format("File %s exists. Overwrite?",file.toString()));
				if (ans == JOptionPane.NO_OPTION) return false;
				if (ans == JOptionPane.CANCEL_OPTION) return true;
			}

			if (!file.getName().endsWith(".xml")) {
				JOptionPane.showMessageDialog(null, "Specified file name does not ends with .xml");
				return true;
			}
			
			String chosen_filename_with_extension = file.getName();
			int index = chosen_filename_with_extension.lastIndexOf('.');
			String chosen_filename = chosen_filename_with_extension.substring(0, index);

			String chosen_folderPath = chosenPathString.substring(0, chosenPathString.length()-chosen_filename_with_extension.length());
			if (spread.getLineDirection() == LineDirection.HORIZONTAL) {
				lineDirectionType = "HORIZONTAL";
			} else if (spread.getLineDirection() == LineDirection.VERTICAL) {
				lineDirectionType = "VERTICAL";
			} else {
				lineDirectionType = "UNDEFINED";
			}
			filetype = spread.getFileNameExtension();
			(new DSCFileMaker(spread)).saveLineSegmentsInXMLFormat(segments, chosen_folderPath, chosen_filename, filetype, lineDirectionType);
			resetChanged();
			return false;
		}
	}
	
	// boolean == true means "canceled"
	public boolean saveInSegfo (boolean forced) {
		Spread spread;
		JFileChooser chooser;
		spread = this.getSpread();
		String filename = spread.getFileNameWithoutExtension();
		String folderpath = Preference.getInstance().getImageFolderPathString() + spread.getFolderPath();
		String defaultFilePathInString = folderpath + spread.getFileNameWithoutExtension() + ".segfo";
		chooser = new JFileChooser(folderpath);
		chooser.setSelectedFile(new File(defaultFilePathInString));	
		chooser.setMultiSelectionEnabled(false);
		chooser.setFileFilter(new SegfoFileFilter());
		chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		
		File file = null;
		
		if (! forced) {
			int input = chooser.showSaveDialog(WorkspaceWindow.getInstance());

			if (input == JFileChooser.CANCEL_OPTION) {
				return true;
			}

			file = chooser.getSelectedFile();
			String chosenPathString = new String();
			try {
				chosenPathString = file.getCanonicalPath();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if (! chosenPathString.equals(defaultFilePathInString) && file.exists()) {
				int ans = JOptionPane.showConfirmDialog(this,String.format("File %s exists. Overwrite?",file.toString()));
				if (ans == JOptionPane.NO_OPTION) return false;
				if (ans == JOptionPane.CANCEL_OPTION) return true;
			}
		}

		if (file == null) return true;
		
		if (!file.getName().endsWith(".segfo")) {
			JOptionPane.showMessageDialog(null, "Specified file name does not ends with .segfo");
			return true;
		}
		
		try{
			FileOutputStream fstream = new FileOutputStream(file);
			OutputStreamWriter  out = new OutputStreamWriter(fstream);
			PrintWriter pw = new PrintWriter(out);

			pw.printf(filename + "%n");

			
			//// TODO 20160101
			if (Preference.getInstance().getTextType()==TextType.HORIZONTAL)
				pw.printf("Horizontal%n");
			else 
				pw.printf("Vertical%n");
			List<LineSegmentForEdit> lines = this.getLinesForEdit();
			for (int i=0;i<lines.size();i++) {
				LineSegmentForEdit line = lines.get(i);
				List<Point2D> points = line.getPoints();
				for (int j=0;j<points.size();j++) {
					Point2D point = points.get(j);
					pw.printf("%6d",(int)point.getX());
					pw.printf("%6d",(int)point.getY());
				}
				pw.printf("%6d%n",-1);
			}
			pw.printf("-99999%n");
			pw.close();
			this.clearUndoRedoStack();
		}catch (Exception e){
			e.printStackTrace();
		}
		resetChanged();
		return false;
	}

	public boolean spreadChangingCancled() {
		return true;
	}
	
	public void setChanged () {
		this.changed = true;
	}
	
	public void resetChanged () {
		this.changed = false;
	}
}
