/* $Id: ImageLabel.java 740 2013-09-09 01:02:09Z yuta-hashimoto $ */
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.Rectangle;
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.util.ArrayList;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JScrollBar;
import javax.swing.SwingUtilities;

import smart_gs.GSConstants;
import smart_gs.drawing_tool.drawing_mode.DefaultDrawingMode;
import smart_gs.drawing_tool.state.DefaultState;
import smart_gs.drawing_tool.state.State;
import smart_gs.drawing_tool.state.UniSelectedState;
import smart_gs.drawing_tool.view.LineSegView;
import smart_gs.drawing_tool.view.View;
import smart_gs.logical.LineSegment;
import smart_gs.logical.Region;
import smart_gs.logical.Spread;
import smart_gs.logical.region.RectangleRegion;
import smart_gs.reasoning_web.logical.GS_RWRepository;
import smart_gs.swingui.GSMouseEvent;
import smart_gs.swingui.WorkspaceWindow;
import sml_editor.logical.LineDirection;

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

	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 static State state;
	private static boolean isLineShown = false;

	private static boolean isRectangleShown = true;
	
	public enum Show{NEITHER,LINE_AND_INDEX,LINE,INDEX};
	private Show lineSegShowingMode = Show.NEITHER;

	private SpreadCanvas canvas;
	private Spread spread;
	private ImageIcon imageIcon;

	private int currentWidth;
	private int currentHeight;

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

	private List<View> tempViews;
	// private static List<Region> tempRegion ;

	// 2007/11/19 kazuhiro kobayashi
	private final Rectangle rect = new Rectangle();
	private Point2D start = null;
	private Point2D end = null;

	static {
		ImageLabel.setState(DefaultState.getInstance());
		isLineShown = false;
	}

	public ImageLabel(SpreadCanvas canvas, ImageIcon imageIcon) {
		super(imageIcon);
		this.canvas = canvas;
		this.spread = this.canvas.getSpread();
		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.addMouseWheelListener(this);
		//
		this.showImage();
		this.setLayout(new FlowLayout(FlowLayout.LEFT));
	}
	
	public SpreadCanvas getSpreadCanvas() {
		return this.canvas;
	}

	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 = spread.getWidth();
		int w = DEFAULT_WIDTH + scale * ZOOM_VALUE;
		double ratio = (double) ((double) w / (double) width);
		List<Region> regions = spread.getRegions();

		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;
		}
		List<LineSegment> lines = spread.getLines();
		if (lines != null) {
			for (int i = 0; i < lines.size(); i++) {
				LineSegView view = (LineSegView) lines.get(i).getView().enlargedView(ratio, gapX,
						gapY);
				view.drawShapeForImageLabel((Graphics2D) g, lineSegShowingMode);
			}
		}
		for (int i = 0; i < regions.size(); i++) {
			View view = regions.get(i).getView()
			.enlargedView(ratio, gapX, gapY);
			if (GS_RWRepository.getInstance().hasLink(regions.get(i))) {
				view.drawLinkedShape((Graphics2D) g, regions.get(i).getLinkedImage());
			} else if (regions.get(i).isTemporary()) {
				view.drawTemporaryShape((Graphics2D) g, new ImageIcon(GSConstants.ICON_PATH + "remove.png").getImage());
			} else {
			view.draw((Graphics2D) g);
		}
	}
		
		for (int i = 0; i < this.tempViews.size(); i++) {
			View view = this.tempViews.get(i).enlargedView(ratio, gapX, gapY);
			view.draw((Graphics2D) g);
		}

		getState().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 = spread.getWidth();
		int w = DEFAULT_WIDTH + scale * ZOOM_VALUE;
		double ratio = ((double) ((double) w / (double) width));
		return ratio;
	}

	public void addRegion(Region region) {
		View view = region.getView();
		region.setView(view);
		spread.addRegion(region);
	}

	/**
	 * 
	 * @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.spread.getWidth();
		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
		if (getState().getClass().equals(UniSelectedState.getInstance().getClass())) {
			if (((UniSelectedState)getState()).isSelectedRegionMoving()) {
				GSMouseEvent ge = this.getAdjustedMouseEvent(e);
				getState().mouseDragged(ge, canvas);
				this.repaint();
				return;
			}
		}
		// 2007/11/20 kazuhiro kobayashi
		this.end = e.getPoint();
		if (DefaultState.getInstance().getMode() == DefaultDrawingMode
				.getInstance()) {
			Rectangle r = this.getVisibleRect();
			this.rect.setRect(r.x + (-this.end.getX() + this.start.getX()), r.y
					- this.end.getY() + this.start.getY(), r.getWidth(), r
					.getHeight());
			this.scrollRectToVisible(this.rect);
		}
		//
		GSMouseEvent ge = this.getAdjustedMouseEvent(e);
		getState().mouseDragged(ge, canvas);
		this.repaint();
	}

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

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

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

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

	public void mousePressed(MouseEvent e) {
		// 2007/11/19 kazuhiro kobayashi
		this.start = e.getPoint();
		//
		// ADD NorthGird 
		if(SwingUtilities.isRightMouseButton(e)){
			for (int i = 0; i < this.tempViews.size(); i++) {
				View view = this.tempViews.get(i);
				if(view.contains(getAdjustedPoint(e.getPoint()))){
					RectangleRegion region = 
						new RectangleRegion(canvas.getSpread(),view.getRectangle());
					canvas.addRegion(region);
				}
			}
		}
		this.tempViews.clear();
		GSMouseEvent ge = this.getAdjustedMouseEvent(e);
		getState().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);
		getState().mouseReleased(ge, canvas);
		this.repaint();
	}

	public static void setState(State state) {
		ImageLabel.state = state;
	}

	public static void setLineVisible(boolean isLineShown) {
		ImageLabel.isLineShown = isLineShown;
		WorkspaceWindow.getInstance().repaint();
	}

	public static boolean isLineShown() {
		return isLineShown;
	}

	/*
	 * 2007/9/11
	 */
	public static void setRectangleVisible(Boolean isRectangleShown) {
		ImageLabel.isRectangleShown = isRectangleShown;
		WorkspaceWindow.getInstance().repaint();
	}

	public static boolean isRectangleShown() {
		return isRectangleShown;
	}

	/**
	 * 
	 * @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
	//2012/01/01 modefied by shayashi
	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;
		JScrollBar hscrollbar = this.canvas.getHorizontalScrollBar();
		int hmax = hscrollbar.getMaximum();
		while (!neighbor((int)this.getLocation().getX(),x) && hscroll < hmax) {
			hscroll++;
			hscrollbar.setValue(hscroll);
		}
		int vscroll = 0;
		JScrollBar vscrollbar = this.canvas.getVerticalScrollBar();
		int vmax = hscrollbar.getMaximum();
		while (!neighbor((int)this.getLocation().getY(),y) && hscroll < vmax) {
			vscroll++;
			vscrollbar.setValue(vscroll);
		}
	}
	
	//2012/01/01/ shayashi
	public void moveToLine(Point point) {
		int offset = 10;
			
		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 (spread.getLineDirection() == LineDirection.HORIZONTAL) {
			x = (int) (offset - (point.x * ratio));
		} else 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 (spread.getLineDirection()==LineDirection.VERTICAL) {
			y = (int) (offset - (point.y * ratio));
		} else 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;
		JScrollBar hscrollbar = this.canvas.getHorizontalScrollBar();
		int hmax = hscrollbar.getMaximum();
		while (!neighbor((int)this.getLocation().getX(),x)) {
			if (hscroll < hmax) hscroll++;
			else break;
			hscrollbar.setValue(hscroll);
		}
		
		
		int vscroll = 0;
		JScrollBar vscrollbar = this.canvas.getVerticalScrollBar();
		int vmax = hscrollbar.getMaximum();
		while (!neighbor((int)this.getLocation().getY(),y)) {
			if (vscroll < vmax) vscroll++;
			else break;
			vscrollbar.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.spread;
	}

	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 static State getState() {
		return state;
	}

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

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

	public int getLineIndex(int x, int y) {
		List<LineSegment> lines = spread.getLines();
		if (lines != null) {
			for (int i = 0; i < lines.size(); i++) {
				LineSegView view = (LineSegView) lines.get(i).getView();
				if (view.contains(new Point(x,y))) return i;
			}
		}
		return -1; /* not found */
	}

	public LineSegment getLineSeg(int n) {
		List<LineSegment> lines = spread.getLines();
		return lines.get(n);
	}
}
