package smart_gs.drawing_tool;

import java.awt.Cursor;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import smart_gs.drawing_tool.drawing_mode.LineSegEditorDrawMode;
import smart_gs.drawing_tool.drawing_mode.LineSegEditorMode;
import smart_gs.drawing_tool.view.LineSegView;
import smart_gs.drawing_tool.view.View;
import smart_gs.logical.LineSegment;
import smart_gs.logical.LineSegmentForEdit;
import smart_gs.logical.Preference;
import smart_gs.logical.Spread;
import smart_gs.smleditor.swingui.LineSegEditor;
import smart_gs.swingui.GSMouseEvent;
import smart_gs.swingui.WorkspaceWindow;
import smart_gs.util.GSLog;
import smart_gs.util.Pair;
import smart_gs.util.SegfoToLineConverter;
import smart_gs.util.XMLToLineConverter;
import sml_editor.logical.LineDirection;

@SuppressWarnings("serial")
public class LineSegEditorImageLabel
	extends JLabel
	implements MouseListener, MouseMotionListener {

	GSLog log = GSLog.getInstance();
	private final int DEFAULT_WIDTH = 1200;
	private final int ZOOM_VALUE = 200;
	//20080617 shimizu wrote
	//private final int ZOOM_VALUE = 400;

	public int scale = 0;

	private List<LineSegmentForEdit> lines = new ArrayList<LineSegmentForEdit>();
	

	// TODO
	public List<LineSegmentForEdit> getSelectedLines() {
		return null;
	}

	public void setSelectedLines(List<LineSegmentForEdit> selectedLines) {
// TODO
	}

	public void addSelectedLine(LineSegmentForEdit selectedLine) {
		// TODO
	}
	
	public void addSelectedLines(List<LineSegmentForEdit> selectedLines) {
		// TODO
	}
	
	private LineSegEditorMode mode;
	private Cursor cursor = Cursor.getDefaultCursor();

	public enum Show{LINE_AND_INDEX,LINE};
	public Show lineSegShowingMode = Show.LINE_AND_INDEX;

	private LineSegEditorCanvas canvas;
	private ImageIcon imageIcon;

	private int currentWidth;
	private int currentHeight;

	private int imageWidth;
	private int imageHeight;
	private Image image;

	private List<View> tempViews;

	private Point2D start = null;
	private Point2D end = null;
	private LineSegEditorDrawMode drawMode;
//	HERE
//	private LineSegEditorUniSelectionState drawMode;
	
//	HERE
//	private LineSegEditorDrawMode drawMode;
//	private LineSegEditorAutoDrawMode autoDrawMode;
//	private LineSegEditorEraseMode eraserMode;

	/**
	 * @return the drawMode
	 */
	public LineSegEditorDrawMode getDrawMode() {
		return this.drawMode;
	}
	
	public LineSegEditorImageLabel(LineSegEditorCanvas canvas, ImageIcon imageIcon,LineSegEditor editor) {
		super(imageIcon);
		this.drawMode = new LineSegEditorDrawMode(editor);
		this.mode = this.drawMode;
		this.canvas = canvas;
		this.imageIcon = imageIcon;
		this.setImageWidth(this.imageIcon.getIconWidth());
		this.setImageHeight(this.imageIcon.getIconHeight());
		this.image = this.imageIcon.getImage();
		this.tempViews = new ArrayList<View>();
		this.addMouseListener(this);
		this.addMouseMotionListener(this);		
		this.lines = this.loadLinesForEditFromLineSegInformationFile();
		if (this.lines == null) log.error("Failed to read line information");
		this.showImage();
		this.setLayout(new FlowLayout(FlowLayout.LEFT));
		if (cursor != null) {
			this.setCursor(cursor);
		}
	}
	
	public LineSegEditorImageLabel(LineSegEditorCanvas canvas, ImageIcon imageIcon,LineSegEditor editor,List<LineSegmentForEdit> lines) {
		super(imageIcon);
		this.drawMode = new LineSegEditorDrawMode(editor);
		this.mode = this.drawMode;
		this.canvas = canvas;
		this.imageIcon = imageIcon;
		this.setImageWidth(this.imageIcon.getIconWidth());
		this.setImageHeight(this.imageIcon.getIconHeight());
		this.image = this.imageIcon.getImage();
		this.tempViews = new ArrayList<View>();
		this.addMouseListener(this);
		this.addMouseMotionListener(this);		
		this.lines = lines;
		this.showImage();
		this.setLayout(new FlowLayout(FlowLayout.LEFT));
		if (cursor != null) {
			this.setCursor(cursor);
		}
	}

	public void showImage() {
		double ratio = (double) getImageWidth() / (double) getImageHeight();
		int w = DEFAULT_WIDTH + scale * ZOOM_VALUE;
		this.setCurrentWidth(w);
		this.setCurrentHeight((int) (w / ratio));
		BufferedImage bufImage = new BufferedImage(this.getCurrentWidth(),
				this.getCurrentHeight(), BufferedImage.TYPE_INT_RGB);
		Graphics offg = bufImage.getGraphics();
		offg
				.drawImage(image, 0, 0, this.getCurrentWidth(), this.getCurrentHeight(),
						this);
		this.setSize(this.getCurrentWidth(), this.getCurrentHeight());
		this.setIcon(new ImageIcon(bufImage));
		WorkspaceWindow.getInstance().getImageToolBar().enableIcons();
	}

	public Point getCornerPoint() {
		return this.getLocation();
	}

	public void zoomIn() {
		// 2007/11/19 kazuhiro kobayashi
		if (this.scale < 21) {
			this.scale++;
			this.showImage();
		}
	}

	public void zoomOut() {
		// 2007/11/19 kazuhiro kobayashi
		if (this.scale > -5) {
			this.scale--;
			this.showImage();
		}
	}

	public void fullSize() {
		this.scale = 0;
		this.showImage();
	}

	public void fitWidth(int width) {
		this.scale = (width - DEFAULT_WIDTH) / ZOOM_VALUE - 1;
		this.showImage();
	}

	public void fitHeight(int height) {
		double ratio = (double) getImageWidth() / (double) getImageHeight();
		this.scale = (int) ((ratio * (double) height - DEFAULT_WIDTH) / ZOOM_VALUE - 1);
		this.showImage();
	}
	public void setScale(int scale) {
		this.scale = scale;
	}

	@Override
	public void paint(Graphics g) {
		super.paint(g);

		int width = this.canvas.getImageIcon().getIconWidth();
		int w = DEFAULT_WIDTH + scale * ZOOM_VALUE;
		double ratio = (double) ((double) w / (double) width);

		int canvasWidth = this.canvas.getWidth();
		int canvasHeight = this.canvas.getHeight();
		int iconHeight = this.getIcon().getIconHeight();
		int iconWidth = this.getIcon().getIconWidth();

		int scrollHeight = this.canvas.getHorizontalScrollBar().getHeight();
		int scrollWidth = this.canvas.getVerticalScrollBar().getWidth();
		double gapX = 0;
		double gapY = 0;

		if (iconWidth < canvasWidth) {
			gapX = (canvasWidth - scrollWidth - iconWidth) / 2.0;
		}
		if (iconHeight < canvasHeight) {
			gapY = (canvasHeight - scrollHeight - iconHeight) / 2.0;
		}

		if (lines != null ) {
			for (int i = 0; i < lines.size(); i++) {
				LineSegView view = (LineSegView) lines.get(i).getView().enlargedView(ratio,gapX,gapY);
				view.drawShapeForLineSegImageLabel((Graphics2D) g, lineSegShowingMode);
			}
		}
		// This prints the incomplete line segment drawn.
		this.mode.paint(g, canvas);
	}

	public double getGapWidth() {
		int canvasWidth = this.canvas.getWidth();
		int iconWidth = this.getIcon().getIconWidth();
		int scrollWidth = this.canvas.getVerticalScrollBar().getWidth();
		double gapX = 0;
		if (iconWidth < canvasWidth) {
			gapX = (canvasWidth - scrollWidth - iconWidth) / 2.0;
		}
		return gapX;
	}

	public double getGapHeight() {
		int canvasHeight = this.canvas.getHeight();
		int iconHeight = this.getIcon().getIconHeight();
		int scrollHeight = this.canvas.getHorizontalScrollBar().getHeight();
		double gapY = 0;
		if (iconHeight < canvasHeight) {
			gapY = (canvasHeight - scrollHeight - iconHeight) / 2.0;
		}
		return gapY;
	}

	public double getRatio() {
		int width = this.canvas.getImageIcon().getIconWidth();
		int w = DEFAULT_WIDTH + scale * ZOOM_VALUE;
		double ratio = ((double) ((double) w / (double) width));
		return ratio;
	}


	/**
	 *
	 * @param point
	 *
	 * @return
	 */
	public Point getAdjustedPoint(Point point) {
		double x = point.x;
		double y = point.y;
		int canvasWidth = this.canvas.getWidth();
		int canvasHeight = this.canvas.getHeight();
		int iconHeight = this.getIcon().getIconHeight();
		int iconWidth = this.getIcon().getIconWidth();

		double gapX = 0;
		double gapY = 0;
		int scrollHeight = this.canvas.getHorizontalScrollBar().getHeight();
		int scrollWidth = this.canvas.getVerticalScrollBar().getWidth();
		if (iconWidth < canvasWidth) {
			gapX = (double) (canvasWidth - scrollWidth - iconWidth) / 2.0;

		}
		if (iconHeight < canvasHeight) {
			gapY = (canvasHeight - scrollHeight - iconHeight) / 2.0;

		}

		x -= gapX;
		y -= gapY;

		int width = this.canvas.getImageIcon().getIconWidth();
		int w = iconWidth;
		double ratio = (double) ((double) w / (double) width);
		Point p = new Point((int) (x / ratio), (int) (y / ratio));
		return p;
	}

	private GSMouseEvent getAdjustedMouseEvent(MouseEvent e) {
		Point p = this.getAdjustedPoint(e.getPoint());
		return new GSMouseEvent(e, p);
	}

	public void mouseDragged(MouseEvent e) {
		// 2010/11/24 kukita

		this.repaint();
	}

	public void mouseMoved(MouseEvent e) {
		GSMouseEvent ge = this.getAdjustedMouseEvent(e);
		this.mode.mouseMoved(ge, canvas);
		this.repaint();
	}

	public void mouseClicked(MouseEvent e) {
		Cursor cursor = this.getCursor();
		GSMouseEvent ge = this.getAdjustedMouseEvent(e);
		this.mode.mouseClicked(ge, canvas);
		this.repaint();
		this.setCursor(cursor);
	}

	public void mouseEntered(MouseEvent e) {
		GSMouseEvent ge = this.getAdjustedMouseEvent(e);
		this.mode.mouseEntered(ge, canvas);
		this.repaint();
	}

	public void mouseExited(MouseEvent e) {
		GSMouseEvent ge = this.getAdjustedMouseEvent(e);
		this.mode.mouseExited(ge, canvas);
		this.repaint();
	}

	public void mousePressed(MouseEvent e) {
		// 2007/11/19 kazuhiro kobayashi
		this.start = e.getPoint();
		//
		this.tempViews.clear();
		GSMouseEvent ge = this.getAdjustedMouseEvent(e);
		this.mode.mousePressed(ge, canvas);
		this.repaint();
	}

	public void mouseReleased(MouseEvent e) {
		// 2007/11/19 kazuhiro kobayashi
			this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
			this.start = null;
			this.end = null;
		//
		GSMouseEvent ge = this.getAdjustedMouseEvent(e);
		this.mode.mouseReleased(ge, canvas);
		this.repaint();
	}

	/**
	 *
	 * @param point
	 *            original
	 */
	public void setCenterLocation(Point point) {
		double ratio = this.getRatio();
		int x = (int) (point.x * ratio);
		int y = (int) (point.x * ratio);
		this.setLocation(-x + 100, -y + 100);
	}

	//2011/01/25/ kukita
	public void moveTo(Point point) {
		int locX = (int)this.getLocation().getX();
		int locY = (int)this.getLocation().getY();

		int cw = this.canvas.getWidth();
		int ch = this.canvas.getHeight();
		int sh = this.canvas.getHorizontalScrollBar().getHeight();
		int sw = this.canvas.getVerticalScrollBar().getWidth();
		int iw = this.getIcon().getIconWidth();
		int ih = this.getIcon().getIconHeight();
		int x;
		int y;
		double ratio = this.getRatio();
		if (iw < cw) {
			x = locX;
		} else {
			x = (int) (cw/2 - (point.x * ratio));
			if (x > 0) {
				x = 0;
			} else if (x < cw - (sw + iw)) {
				x = cw - (sw + iw);
			}
		}
		if (ih < ch) {
			y = locY;
		} else {
			y = (int) (ch/2 - (point.y * ratio));
			if (y > 0) {
				y = 0;
			} else if (y < ch - (sh + ih)) {
				y = ch - (sh + ih);
			}
		}

		int hscroll = 0;
		while (!neighbor((int)this.getLocation().getX(),x)) {
			hscroll++;
			this.canvas.getHorizontalScrollBar().setValue(hscroll);
		}
		int vscroll = 0;
		while (!neighbor((int)this.getLocation().getY(),y)) {
			vscroll++;
			this.canvas.getVerticalScrollBar().setValue(vscroll);
		}

	}

	private static boolean neighbor(int n, int m) {
		return ((n - m) * (n - m)) <=1 ;
	}


	public void addTempView(View view) {
		this.tempViews.add(view);
	}

	/**
	 *
	 *
	 * @return
	 */
	public Spread getSpread() {
		return this.canvas.getSpread();
	}

	public void setCurrentWidth(int currentWidth) {
		this.currentWidth = currentWidth;
	}

	public int getCurrentWidth() {
		return currentWidth;
	}

	public void setCurrentHeight(int currentHeight) {
		this.currentHeight = currentHeight;
	}

	public int getCurrentHeight() {
		return currentHeight;
	}

	public void setImageWidth(int imageWidth) {
		this.imageWidth = imageWidth;
	}

	public int getImageWidth() {
		return imageWidth;
	}

	public void setImageHeight(int imageHeight) {
		this.imageHeight = imageHeight;
	}

	public int getImageHeight() {
		return imageHeight;
	}

//	public LineSegEditorState getState() {
//		return state;
//	}
//
//	public void setState(LineSegEditorState state){
//		this.state = state;
//	}

	public void setMode(LineSegEditorMode mode){
		this.mode = mode;
//		this.state.setMode(mode);
	}

	public Show getLineSegShowingMode() {
		return lineSegShowingMode;
	}
	public void setLineSegShowingMode(Show mode) {
		lineSegShowingMode = mode;
	}

	public void stepLineSegShowingMode(Show mode) {
		if (mode == Show.LINE_AND_INDEX) {
			lineSegShowingMode = Show.LINE;
		} else if (mode == Show.LINE) {
			lineSegShowingMode = Show.LINE_AND_INDEX;
		}
	}


	public void rewriteLinesSegIndexes() {
		for (int i = 0; i<lines.size();i++){
			lines.get(i).setIndex(i);
		}
	}


	public void addLineSegmentForEdit(LineSegmentForEdit LineSegmentForEdit) {
		this.lines.add(LineSegmentForEdit);
	}

	/**
	 * @return the lines
	 */
	public List<LineSegmentForEdit> getLines() {
		return this.lines;
	}

	public List<LineSegmentForEdit> loadLinesForEditFromLineSegInformationFile() {
		String filenameBody = Preference.getInstance().getDscFolderPathString() + this.canvas.getSpread().getSpreadDirParent().getPath()
		+ this.canvas.getSpread().getFileNameWithoutExtension();
		
		String filenameXml = filenameBody + ".xml";
		File fileXml = new File(filenameXml);
		String filenameSegfo = filenameBody + ".segfo";
		File fileSegfo = new File(filenameSegfo);
		String filename;
		Pair<LineDirection,List<LineSegmentForEdit>> tmp = null;
		
		if (fileXml.exists()) {
			tmp = new XMLToLineConverter(this.canvas.getSpread()).getLinesForEdit(fileXml);
			if (tmp == null) {
				log.error("Failed to read line segment file %s" + filenameXml);
				return null;
			}
			filename = filenameXml;
				
		} else if (fileSegfo.exists()) {
			tmp = new SegfoToLineConverter(this.canvas.getSpread()).getLinesForEdit(fileSegfo,filenameSegfo);
			if (tmp == null) {
				log.error("Failed to read line segment file " + filenameSegfo);
				return null;
			}
			filename = filenameSegfo;
		} else {
			Object[] possibilities = {"horizontal text in xml", "vertical text in xml", "horizontal text in segfo", "vertical text in segfo",};
			String s = (String)JOptionPane.showInputDialog(
					this,
					"No line info. file. Creating a new file.\n" +
					"Choose its type.",
					"Create Line Info. File",
					JOptionPane.PLAIN_MESSAGE,
					null,
					possibilities,
					"horizontal text in xml");
			
			File fileToCreate = fileXml;
			
			String contentStringXml = "";
			
			contentStringXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n";
			contentStringXml += "<segmentInfo>\n";
			contentStringXml += "<source>";
			
			String stringToWrite = new String();
			
			if (s.equals("horizontal text in xml")) {
				fileToCreate = fileXml;
				stringToWrite += contentStringXml;
				stringToWrite += filenameXml;
				stringToWrite += "</source>";
				stringToWrite += "\n<lineDirection>HORIZONTAL</lineDirection>\n<segments/></segmentInfo>";
			} else if (s.equals("vertical text in xml")) {
				fileToCreate = fileXml;
				stringToWrite += contentStringXml;
				stringToWrite += filenameXml;
				stringToWrite += "</source>";
				stringToWrite += "\n<lineDirection>VERTICAL</lineDirection>\n<segments/></segmentInfo>";
			} else if (s.equals("horizontal text in segfo")) {
				fileToCreate = fileSegfo;
				stringToWrite = this.canvas.getSpread().getFileName();
				stringToWrite += "\nHORIZONTAL";
				stringToWrite += "\n-99999";
			} else if (s.equals("vertical text in segfo")) {
				fileToCreate = fileSegfo;
				stringToWrite = this.canvas.getSpread().getFileName();
				stringToWrite += "\nVERTICAL";
				stringToWrite += "\n-99999";
			}
			
			System.out.println(stringToWrite);
			
//			Try to make file
			try {
				fileToCreate.createNewFile();
			} catch (IOException e) {
				log.error("Could not make line info. file:" + fileToCreate.getName());
				return null;
			}

//			Try to write contents
			PrintWriter writer;
			try {
				writer = new PrintWriter(fileToCreate, "UTF-8");
			} catch (FileNotFoundException e1) {
				log.error("Could not make line info. file:" + fileToCreate.getName());
			} catch (UnsupportedEncodingException e1) {
				log.error("Unkown error occured when creating " + fileToCreate.getName());
			}
			
			FileWriter filewriter;
			try {
				filewriter = new FileWriter(fileToCreate, true);
	        	filewriter.write(stringToWrite);
	        	filewriter.close();
			} catch (IOException e) {
				log.error("IOException " + e.toString() + " occured when writing" + fileToCreate.getName());
				return null;
			}
		}
			
		if (fileXml.exists()) {
			tmp = new XMLToLineConverter(this.canvas.getSpread()).getLinesForEdit(fileXml);
			if (tmp == null) {
				log.error("Failed to read line segment file " + filenameXml);
				return null;
			}
			filename = filenameXml;
				
		} else if (fileSegfo.exists()) {
			tmp = new SegfoToLineConverter(this.canvas.getSpread()).getLinesForEdit(fileSegfo,filenameSegfo);
			if (tmp == null) {
				log.error("Failed to read line segment file " + filenameSegfo);
				return null;
			}
			filename = filenameSegfo;
		}

		return tmp.getRight();
	}
	

	/**
	 * @param lines the lines to set
	 */
	public void setLines(List<LineSegmentForEdit> lines) {
		//this.lines = lines;	// mod 20120923 yamanaka
		if (lines != null) {
			this.lines = lines;
		} else {
			this.lines = new ArrayList<LineSegmentForEdit>();
		}
	}

	public int getLineIndex(int x, int y) {
		// TODO Auto-generated method stub
		return 0;
	}

	public LineSegEditorMode getMode() {
		return this.mode;
	}

	public LineSegEditor getEditor() {
		// TODO Auto-generated method stub
		return null;
	}

	public int setPressedLineSelected(Point2D point2d) {
		int returnvalue =0;
		for (int i=0;i<lines.size();i++) {
			LineSegmentForEdit cl = lines.get(i);
			if (cl.contains(point2d)) {
				cl.setIsSelected(true);
				returnvalue = i;
			} else {
				cl.setIsSelected(false);
			}
		}
		return returnvalue;
		
	}

	public int selectedIndex() {
		int ans = -1;
		for (int i=0;i<lines.size();i++) {
			if (lines.get(i).isSelected()) {
				ans = i;
				break;
			}
		}
		return ans;
	}
}
