package storagesim;

import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;

import javax.swing.JPanel;

/**
 * Draws a {@link StorageMap} inside a {@link javax.swing.JPanel}
 * 
 * @author Manuel
 */
public class MapPanel extends JPanel {
	private static final long serialVersionUID = 1L;
	
	double scale;
	double translateX, translateY;
	
	Point prevMouse;

	StorageWindow parent;
	
	StorageMap map;
	
	public MapPanel(StorageWindow parent) {
		super();
		
		this.parent = parent;
		
		this.scale = 1.0;
		this.translateX = 0.0;
		this.translateY = 0.0;
		
		this.setSize(720, 480);

		map = new StorageMap(24, 15);
		map.updateMap(parent.db);
		
		this.setPreferredSize(this.getSize());
		this.setBackground(Color.MAGENTA);
		
		Controls c = new Controls();
		this.addMouseListener(c);
		this.addMouseMotionListener(c);
		this.addMouseWheelListener(c);
		
		this.addComponentListener(new ComponentAdapter() {
			@Override
			public void componentResized(ComponentEvent e) {
				super.componentResized(e);
				applyTransform(0, 0, 0, 0, 0);
			}
		});
	}
	
	/**
	 * Draws the map inside the panel
	 */
	@Override
	public void paint(Graphics g) {
		super.paint(g);
		
		Graphics2D g2 = (Graphics2D) g;

		g2.translate(this.translateX, this.translateY);
		g2.scale(this.scale, this.scale);
		
		g2.drawImage(this.map.img, 0, 0, null);
		
		if (this.map.selectedTile >= 0) {
			int selectedX = (this.map.selectedTile % this.map.tileColumns) * this.map.tileSize;
			int selectedY = (this.map.selectedTile / this.map.tileColumns) * this.map.tileSize;
			
			g2.setColor(Color.YELLOW);
			g2.drawRect(selectedX, selectedY, this.map.tileSize, this.map.tileSize);
		}
		
		// TODO bot entities
	}
	
	/**
	 * Reset the view
	 */
	public void resetView() {
		this.scale = 1.0;
		this.translateX = 0.0;
		this.translateY = 0.0;
		
		this.applyTransform(0, 0, 0, 0, 0);
	}
	
	/**
	 * PURE FUCKING MAGIC; DON'T TOUCH!
	 */
	private void applyTransform(double dTranslateX, double dTranslateY, double dScale, int mouseX, int mouseY) {		
		this.scale += dScale;
		
		double scaleMinX = (double)getWidth() / map.img.getWidth();
		double scaleMinY = (double)getHeight() / map.img.getHeight();
		double scaleMin = scaleMinX < scaleMinY ? scaleMinY : scaleMinX;
		
		double scaleMax = 24;
				
		// limit scale to image limits
		if(this.scale < scaleMin) {
			dScale = (this.scale - dScale) % scaleMin;
			this.scale = scaleMin;
		} else if (this.scale > scaleMax) {
			dScale -= (this.scale - scaleMax);
			this.scale = scaleMax;
		}
		
		// adjust position according to zoom: (new pos - prev pos) * scale
		this.translateX += (((mouseX - this.translateX) / this.scale) - 
				((mouseX - this.translateX) / (this.scale - dScale))) * this.scale;
		this.translateY += (((mouseY - this.translateY) / this.scale) - 
				((mouseY - this.translateY) / (this.scale - dScale))) * this.scale;
		
		this.translateX += dTranslateX;
		this.translateY += dTranslateY;
		
		// limit translation to image borders
		if (this.translateX > 0) {
			this.translateX = 0;
		} else if (this.translateX < (getWidth() - (map.img.getWidth() * this.scale))) {
			this.translateX = getWidth() - (map.img.getWidth() * this.scale);
		}
		if (this.translateY > 0) {
			this.translateY = 0;
		} else if (this.translateY < (getHeight() - (map.img.getHeight() * this.scale))) {
			this.translateY = getHeight() - (map.img.getHeight() * this.scale);
		}
	}

	/**
	 * Holds the interaction controls
	 */
	private class Controls extends MouseAdapter {
		/**
		 * Updates mouse position on click
		 * 
		 * @param e mouse event with coordinates
		 */
		@Override
		public void mousePressed(MouseEvent e) {
			prevMouse = e.getPoint();
			repaint();
		}
		
		/**
		 * Listens to mouse drags to transform the map
		 * 
		 * @param e mouse event with coordinates
		 */
		@Override
		public void mouseDragged(MouseEvent e) {
			if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == MouseEvent.BUTTON1_DOWN_MASK) {
				applyTransform(
						e.getX() - prevMouse.getX(),
						e.getY() - prevMouse.getY(),
						0.0, e.getX(), e.getY());
				
				prevMouse = e.getPoint();
				repaint();
			}
		}
		
		/**
		 * Listens to scroll to scale the map
		 * 
		 * @param e mouse event with scroll information
		 */
		@Override
		public void mouseWheelMoved(MouseWheelEvent e) {
            if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
				double scaleChange = e.getPreciseWheelRotation() * (scale / -16.0);
				
				applyTransform(0.0, 0.0, scaleChange, e.getX(), e.getY());
				
				repaint();
            }
		}
		
		/**
		 * Listens to mouse clicks to change which tile is in focus
		 * 
		 * @param e mouse event with position information
		 */
		public void mouseClicked(MouseEvent e) {
			double xOnMap = (e.getX() - translateX) / scale;
			double yOnMap = (e.getY() - translateY) / scale;
			
			int xTile = (int) (xOnMap / map.tileSize);
			int yTile = (int) (yOnMap / map.tileSize);
			
			map.selectedTile = yTile * map.tileColumns + xTile;
			parent.updateSidebar(parent.db);
			
			repaint();
		}
	}
}
