/**
 * @version $Id: ZoomControl.java 22513 2011-04-15 21:19:24Z teetov $
 * ------------------------------------------------------------------------------
 * This class represents ZoomControl class for manipulating with zoomed image
 * ------------------------------------------------------------------------------
 * @author Andrey Starostin
 * @QA
 * @copyright videoNEXT Network Solutions LLC 2010
 * ------------------------------------------------------------------------------
 */

package com.videonext.mplayer.internal;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;

/**
 * Class ZoomControl
 */
class ZoomControl implements MouseListener, MouseMotionListener, MouseWheelListener
{
	private static final float imageZoomFactorDX = 0.05f;

	private Rectangle imageRect = new Rectangle(0, 0, 1, 1);
	private Point currentMousePos = new Point(0, 0);
	private Point lastDraggedMousePos = new Point(0, 0);
	private Point mouseInLittleRectClickPos = new Point(0, 0);
	private boolean isMouseLeftButtonPressed = false;

	private float minImageZoomFactor = 0.1f;
	private final float maxImageZoomFactor = 2.0f;

	private Rectangle smallRect = new Rectangle(0, 0, 1, 1);
	private Rectangle littleRect = new Rectangle(0, 0, 1, 1);
	private float imageZoomFactor = 1.5f;

	private Rectangle closeButtonRect = new Rectangle(0, 0, 12, 12);

	private boolean isZoom = false;

	private MediaPlayerListener listener;
	private Component canvas;
	private Rectangle screenRect;
	private BufferedImage im;
	private float pixelAspectRatio = 0f;

	public void setMediaPlayerListener(MediaPlayerListener listener)
	{
		this.listener = listener;
	}

	public void setScreenRect(Rectangle screenRect)
	{
		this.screenRect = screenRect;
	}

	public void setCanvas(Component canvas)
	{
		this.canvas = canvas;
	}

	public void setIm(BufferedImage im)
	{
		this.im = im;
	}

	public void setPixelAspectRatio(float pixelAspectRatio)
	{
		this.pixelAspectRatio = pixelAspectRatio;
	}

	public Rectangle getSmallRect()
	{
		return this.smallRect;
	}

	//
	// MouseListener implementation
	//
	public void mouseClicked(MouseEvent e) {
		currentMousePos.setLocation(e.getX(), e.getY());

		if (!littleRect.contains(currentMousePos) && !closeButtonRect.contains(currentMousePos))
		{
			listener.mouseClicked(e, true);
		}
	}

	public void mousePressed(MouseEvent e) {
		currentMousePos.setLocation(e.getX(), e.getY());

		if(e.getButton() == MouseEvent.BUTTON1) {

			if (littleRect.contains(currentMousePos))
			{
				isMouseLeftButtonPressed = true;
				mouseInLittleRectClickPos.setLocation(currentMousePos.x - littleRect.x, currentMousePos.y - littleRect.y);
				calculateZoomRectangles();
			}

			// close button
			if (closeButtonRect.contains(currentMousePos))
			{
				isZoom = false;
			}
		}
	}

	public void mouseReleased(MouseEvent e) {
		if(e.getButton() == MouseEvent.BUTTON1)	{
			isMouseLeftButtonPressed = false;
			this.canvas.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		}
	}

	public void mouseEntered(MouseEvent e) {
		this.canvas.repaint();
		
		//listener.mouseMoved(e, true);
	}

	public void mouseExited(MouseEvent e) {
		this.canvas.repaint();
		
		listener.mouseExit(e, true);
	}

	//
	// MouseMotionListener implementation
	//
	public void mouseDragged(MouseEvent e) {
		currentMousePos.setLocation(e.getX(), e.getY());

		if (isZoom && isMouseLeftButtonPressed)
		{
			lastDraggedMousePos.setLocation(currentMousePos);

			calculateZoomRectangles();

			this.canvas.repaint();
			this.canvas.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
		}
	}

	public void mouseMoved(MouseEvent e){
		currentMousePos.setLocation(e.getX(), e.getY());

		int cursorType = Cursor.DEFAULT_CURSOR;
		if (isZoom)
		{
			// move little rectangle in small image
			if (littleRect.contains(currentMousePos))
			{
				cursorType = Cursor.MOVE_CURSOR;
			}

			// close button
			if (closeButtonRect.contains(currentMousePos))
			{
				cursorType = Cursor.HAND_CURSOR;
			}
		}
		this.canvas.setCursor(Cursor.getPredefinedCursor(cursorType));
				
		listener.mouseMoved(e, true);
	}

	//
	// MouseWheelListener implementation
	//
	public void mouseWheelMoved(MouseWheelEvent e)
	{
		calculateZoomRectangles();
		int rotation = e.getWheelRotation();
		if (rotation < 0) {
			calculateImageZoomFactor(imageZoomFactorDX);
		} else if (rotation > 0) {
			calculateImageZoomFactor(-imageZoomFactorDX);
		}
		calculateImageRectSize();
		this.canvas.repaint();
	}


	public boolean isZoomed() {
		return isZoom;
	}

	public float getImageZoomFactor() {
		return imageZoomFactor;
	}

	//
	// Zooming
	//
	public Rectangle getPartRect()
	{
		float xFactor = 1, yFactor = 1;

		if (pixelAspectRatio > 1.0)
		{
			xFactor = pixelAspectRatio;
		}
		else if (pixelAspectRatio < 1 && pixelAspectRatio != 0)
		{
			yFactor = 1 / pixelAspectRatio;
		}

		return new Rectangle(
			(int)(xFactor * -imageRect.x / imageZoomFactor),
			(int)(yFactor * -imageRect.y / imageZoomFactor),
			(int)(xFactor * (-imageRect.x + screenRect.width) / imageZoomFactor),
			(int)(yFactor * (-imageRect.y + screenRect.height) / imageZoomFactor)
		);
	}

	public Boolean paint(Graphics g, Drawable zoomControlDraw)
	{
		Boolean wasDraw = false;

		if (imageRect.width != screenRect.width || imageRect.height != screenRect.height)
		{
			wasDraw = true;

			// draw little copy of full image to screen
			g.drawImage(im, smallRect.x - screenRect.x, smallRect.y - screenRect.y, smallRect.width, smallRect.height, null);

			// draw some additional graphic over small image
			if (zoomControlDraw != null) {
				zoomControlDraw.draw(g, new Rectangle(smallRect.x - screenRect.x, smallRect.y - screenRect.y, smallRect.width, smallRect.height));
			}

			// draw borders around little rectangle
			Color color = new Color(100, 100, 100, 200);
			g.setColor(color);
			// top border
			g.fillRect(smallRect.x - screenRect.x, smallRect.y - screenRect.y, smallRect.width, littleRect.y - smallRect.y);
			// right border
			g.fillRect(littleRect.x + littleRect.width - screenRect.x, smallRect.y - screenRect.y, (smallRect.x + smallRect.width) - (littleRect.x + littleRect.width), smallRect.height);
			// bottom border
			g.fillRect(smallRect.x - screenRect.x, littleRect.y + littleRect.height - screenRect.y, smallRect.width, (smallRect.y + smallRect.height) - (littleRect.y + littleRect.height));
			// left border
			g.fillRect(smallRect.x - screenRect.x, smallRect.y - screenRect.y, littleRect.x - smallRect.x, smallRect.height);

			// draw small rectangle
			g.setColor(Color.GREEN);
			g.drawRect(smallRect.x - screenRect.x, smallRect.y - screenRect.y, smallRect.width, smallRect.height);

			// draw close button on small rectangle
			g.drawRect(closeButtonRect.x - screenRect.x, closeButtonRect.y - screenRect.y, closeButtonRect.width, closeButtonRect.height);
			g.drawLine(closeButtonRect.x - screenRect.x, closeButtonRect.y - screenRect.y, closeButtonRect.x - screenRect.x + closeButtonRect.width, closeButtonRect.y - screenRect.y + closeButtonRect.height);
			g.drawLine(closeButtonRect.x - screenRect.x, closeButtonRect.y - screenRect.y + closeButtonRect.height, closeButtonRect.x - screenRect.x + closeButtonRect.width, closeButtonRect.y - screenRect.y);

			// draw border in little copy that represent visible part of full image
			g.drawRect(littleRect.x - screenRect.x, littleRect.y - screenRect.y, littleRect.width, littleRect.height);
		}

		return wasDraw;
	}

	public void calculateImageRectSize()
	{
		if (this.im == null)
		{
			System.out.println("im is null");
			return;
		}

		calculateZoomRectangles();
		calculateImageZoomFactor(0.0f);

		Rectangle newImageRect = new Rectangle();
		newImageRect.width = (int)(im.getWidth() * imageZoomFactor);
		newImageRect.height = (int)(im.getHeight() * imageZoomFactor);

		if (pixelAspectRatio > 1.0)
		{
			newImageRect.width /= pixelAspectRatio;
		}
		else if (pixelAspectRatio < 1 && pixelAspectRatio != 0)
		{
			newImageRect.height *= pixelAspectRatio;
		}

		imageRect.width = newImageRect.width;
		imageRect.height = newImageRect.height;
		calculateZoomRectangles();
	}

	private void calculateImageZoomFactor(float factorDX)
	{
		if (this.im == null)
		{
			System.out.println("im is null");
			return;
		}

		// calculate minImageZoomFactor
		minImageZoomFactor = (float)screenRect.height / (float)im.getHeight();

		float newImageZoomFactor = imageZoomFactor;
		boolean newIsZoom = isZoom;

		if (!isZoom)
		{
			newIsZoom = true;
			newImageZoomFactor = minImageZoomFactor;
		}

		if (minImageZoomFactor < maxImageZoomFactor)
		{
			newImageZoomFactor += factorDX;
			if (newImageZoomFactor > maxImageZoomFactor)
			{
				newImageZoomFactor = maxImageZoomFactor;
			} else
			if (newImageZoomFactor < minImageZoomFactor + 0.01f)
			{
				newImageZoomFactor = minImageZoomFactor;
				newIsZoom = false;
			}

			imageZoomFactor = newImageZoomFactor;
			isZoom = newIsZoom;
		} else {
			isZoom = false;
		}
	}

	private void calculateZoomRectangles()
	{
		smallRect.width = screenRect.width / 3;
		smallRect.height = screenRect.height / 3;
		smallRect.x = screenRect.x + screenRect.width - smallRect.width - 3;
		smallRect.y = screenRect.y + screenRect.height - smallRect.height - 3;

		closeButtonRect.x = smallRect.x + smallRect.width - closeButtonRect.width;
		closeButtonRect.y = smallRect.y - closeButtonRect.height;

		if (smallRect.width != 0 && smallRect.height != 0)
		{
			Point newImagePos = new Point();
			newImagePos.x = - (lastDraggedMousePos.x - mouseInLittleRectClickPos.x - smallRect.x) * imageRect.width / smallRect.width;
			newImagePos.y = - (lastDraggedMousePos.y - mouseInLittleRectClickPos.y - smallRect.y) * imageRect.width / smallRect.width;

			// check for exit of little rectangle from small rectangle
			if (newImagePos.x > 0) newImagePos.x = 0;
			else
			if (newImagePos.x + imageRect.width < screenRect.width) newImagePos.x = screenRect.width - imageRect.width;

			if (newImagePos.y > 0) newImagePos.y = 0;
			else
			if (newImagePos.y + imageRect.height < screenRect.height) newImagePos.y = screenRect.height - imageRect.height;

			imageRect.x = newImagePos.x;
			imageRect.y = newImagePos.y;

			littleRect.x = smallRect.x - imageRect.x * smallRect.width / imageRect.width;
			littleRect.y = smallRect.y - imageRect.y * smallRect.width / imageRect.width;
			littleRect.width = screenRect.width * smallRect.width / imageRect.width;
			littleRect.height = screenRect.width * smallRect.height / imageRect.width;
		}

		/*log.fine(
		" mX = " + lastDraggedMousePos.x + " mY = " + lastDraggedMousePos.y +
		" iX = " + imageRect.x + " iY = " + imageRect.y +
		" iW = " + imageRect.width + " iH = " + imageRect.height +
		" scX = " + screenRect.x + " scY = " + screenRect.y +
		" scW = " + screenRect.width + " scH = " + screenRect.height +
		" smX = " + smallRect.x + " smY = " + smallRect.y +
		" smW = " + smallRect.width + " smH = " + smallRect.height +
		" lX = " + littleRect.x + " lY = " + littleRect.y +
		" lW = " + littleRect.width + " lH = " + littleRect.height
		);*/
	}
}

interface Drawable
{
	public void draw(Graphics g, Rectangle dstRect);
}
