package gui.simulator;

import gui.simulator.facilities.*;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.TexturePaint;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Terrain extends JPanel {

    private List<Draw> entities;
    private Point2D offset;
    private Draw dragObject, selectedObject;
    private BufferedImage background;
    
    private int length;
    private int width;
    private int terrain;
    
    Point2D cameraPoint = new Point2D.Double(1920/2,1080/2);
	float cameraScale = 1;
	
	Point2D lastClickPosition;
	Point lastMousePosition;

    public Terrain(int length, int width, int terrain){
    	//get values
    	this.length = length;
    	this.width = width;
    	this.terrain = terrain;
    	
    	//set terrainbackground
    	switch(terrain){
    	case 1: 
    		try {
			background = ImageIO.read(new File("res/sand.jpg"));
		} catch (IOException e) {
			e.printStackTrace();
		}
    		break;
    	case 2:
    		try {
    			background = ImageIO.read(new File("res/grass.jpg"));
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		break;
    	case 3:
    		try {
    			background = ImageIO.read(new File("res/street.jpg"));
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		break;
    	}
    	
        this.dragObject = null;
        this.entities = new LinkedList();
        this.initEntities();
        this.listeners();
    }

    private void listeners(){
        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
            	Point2D clickPoint = getClickPoint(e.getPoint());
                boolean newObject = false;
                if(e.getX() < 200)
				{
					if(e.getY() < 300){
						dragObject = new Stage((int)clickPoint.getX(),(int)clickPoint.getY(), 0.5, 10);
						offset = new Point2D.Double(e.getX() - dragObject.getX(), e.getY() - dragObject.getY());
					}else if(e.getY() > 300 && e.getY() < 600){
						dragObject = new RestRoom((int)clickPoint.getX(),(int)clickPoint.getY(), 0.5, 10);
						offset = new Point2D.Double(e.getX() - dragObject.getX(), e.getY() - dragObject.getY());
					}
					entities.add(dragObject);
				}
                for(Draw drawObject: entities){
                    if(drawObject.contains(e.getPoint())){
                        dragObject = drawObject;
                        offset = new Point2D.Double(e.getX() - drawObject.getX(), e.getY() - drawObject.getY());
                        selectedObject = dragObject;
                        newObject = true;
                    }
                }
                if(!newObject){
                    selectedObject = null;
                }
                repaint();
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                dragObject = null;
            }
        });

        addMouseMotionListener(new MouseAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
            	Point2D clickPoint = getClickPoint(e.getPoint());
                if(dragObject != null){
                    if(SwingUtilities.isLeftMouseButton(e)){
                            dragObject.setX(e.getX() - offset.getX());
                            dragObject.setY(e.getY() - offset.getY());
                    }else if(SwingUtilities.isRightMouseButton(e)){
                        dragObject.setRotation(e.getX() * -1);
                    }
                }
                else
				{
					cameraPoint = new Point2D.Double(
							cameraPoint.getX() + (lastMousePosition.getX() - e.getX()),
							cameraPoint.getY() + (lastMousePosition.getY() - e.getY())
							);
					repaint();
				}
				lastMousePosition = e.getPoint();
				lastClickPosition = clickPoint;
                repaint();
            }
        });

        addMouseWheelListener(new MouseAdapter() {
            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                for(Draw object: entities){
                    if(object.contains(e.getPoint())){
                        double scale = object.getScale();
                        scale *= 1 + (e.getPreciseWheelRotation() / 10);
                        object.setScale(scale);
                    }else{
                    	cameraScale *= 1 - (e.getPreciseWheelRotation()/10.0);
                    }
                }
                repaint();
            }
        });
        
    }

    private void initEntities(){
        this.entities.add(new Stage(300, 10, 0.5, 10));
        this.entities.add(new Stage(400, 40, 2, 10));
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        Graphics2D g2 = (Graphics2D) g;
        
        //draw sidebar
        g2.setClip(new Rectangle2D.Double(0,0, 200, getHeight()));
        g2.setColor(Color.GRAY);
        g2.fill3DRect(0, 0, 200, 50, true);
        g2.setColor(Color.WHITE);
        g2.setFont(new Font("Serif", Font.BOLD, 20));
        g2.drawString("Drag and place", 30, 25);
        g2.setColor(Color.BLACK);
        g2.setStroke(new BasicStroke(5));
        g2.drawLine(199, 0, 199, getHeight());
        	//draw objects
        Draw object1 = new Stage(0,50);
        object1.draw(g2);
        g2.drawString("stage", 80, 275);
        Draw object2 = new RestRoom(0,300, 0.5, 10);
        object2.draw(g2);
        g2.drawString("toilettage", 70, 525);
		
		//draw simulation field
		g2.setClip(new Rectangle2D.Double(200, 0, getWidth()-200, getHeight()));
        TexturePaint p = new TexturePaint(background, new Rectangle2D.Double(0, 0, 500, 500));
		g2.setPaint(p);
		g2.fill(new Rectangle2D.Double(0,0,width,length));
        for(Draw drawObject: entities){
            drawObject.draw(g2); 
        }

        if(this.selectedObject != null){
            Shape shape = new Rectangle2D.Double(this.selectedObject.getX(), this.selectedObject.getY() , this.selectedObject.getWidth() * this.selectedObject.getScale(), this.selectedObject.getHeight() * this.selectedObject.getScale());
            AffineTransform backup = g2.getTransform();
            AffineTransform trans = new AffineTransform();
            trans.rotate(Math.toRadians(this.selectedObject.getRotation()), (this.selectedObject.getX() + (this.selectedObject.getWidth() / 2 * this.selectedObject.getScale())),(this.selectedObject.getY() +  (this.selectedObject.getHeight() /2 * this.selectedObject.getScale())));
            g2.transform(trans);
            g2.setColor(Color.RED);
            g2.draw(shape);
            g2.transform(backup);

            boolean collide = false;
            for(Draw draw: entities){
                if(!draw.equals(this.selectedObject)){
                    if(intersection(draw, this.selectedObject)){
                        collide = true;
                    }
                }
            }

            if(collide){
                g2.setColor(new Color(254, 0, 0, 128));

            }else{
                g2.setColor(new Color(0, 254, 0, 128));

            }

            g2.fill(this.drawDragRectangle(this.selectedObject));
            g2.setClip(null);
        }
    }

    private Rectangle drawDragRectangle(Draw shape){
        Rectangle rectangle = null;
        rectangle = new Rectangle((int) (shape.getX() - shape.getDistanceToOtherObjects()), (int) (shape.getY() - shape.getDistanceToOtherObjects()), (int) (shape.getWidth() * shape.getScale() + (2 * shape.getDistanceToOtherObjects())), (int) (shape.getHeight() * shape.getScale() + (2 * shape.getDistanceToOtherObjects())));
        return rectangle;
    }

    private boolean intersection(Draw d1, Draw d2){
        Rectangle2D.Double s1 = new Rectangle2D.Double(d1.getX() - d1.getDistanceToOtherObjects(), d1.getY() - d1.getDistanceToOtherObjects(), d1.getWidth() * d1.getScale() + (2*d1.getDistanceToOtherObjects()) , d1.getHeight() * d1.getScale() + (2*d1.getDistanceToOtherObjects()));
        Rectangle2D.Double s2 = new Rectangle2D.Double(d2.getX() - d2.getDistanceToOtherObjects(), d2.getY() - d2.getDistanceToOtherObjects(), d2.getWidth() * d2.getScale() + (2*d2.getDistanceToOtherObjects()) , d2.getHeight() * d2.getScale() + (2*d2.getDistanceToOtherObjects()));
        return s1.intersects(s2);
    }
    
    private Point2D getClickPoint(Point point) {
		try {
			return getCamera().inverseTransform(point, null);
		} catch (NoninvertibleTransformException e1) {
			e1.printStackTrace();
		}
		return null;
	}
    
    private AffineTransform getCamera() {
		AffineTransform tx = new AffineTransform();
		tx.translate(-cameraPoint.getX() + getWidth()/2, -cameraPoint.getY() + getHeight()/2);
		tx.scale(cameraScale, cameraScale);
		
		return tx;
	}
}
