package gui.simulator;

import gui.simulator.Images.ImageType;
import gui.simulator.facilities.Fence;
import gui.simulator.facilities.PalmTree1;
import gui.simulator.facilities.RestRoom;
import gui.simulator.facilities.SimulatorStage;
import gui.simulator.facilities.SnackBar;
import gui.simulator.facilities.Tree1;
import gui.simulator.facilities.WayPoint;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map.Entry;

public class Sidebar implements Serializable  {
	private int sideBarWidth;
	private int sideBarHeight;
	private Terrain terrain;
	private LinkedHashMap<String, DrawEngine> drawableStages = new LinkedHashMap<String, DrawEngine>();
	private LinkedHashMap<String, DrawEngine> drawableFacilities = new LinkedHashMap<String, DrawEngine>();
	private LinkedHashMap<String, DrawEngine> drawableOrnaments = new LinkedHashMap<String, DrawEngine>();
	private LinkedHashMap<String, Images.ImageType> drawablePaths = new LinkedHashMap<String, Images.ImageType>();
	private ArrayList<LinkedHashMap<String, DrawEngine>> drawableArrays = new ArrayList<LinkedHashMap<String, DrawEngine>>();
	public enum SideBarTab{
		STAGES(0), FACILITIES(1), ORNAMENTS(2), PATHS(3);
		
		public final int value;
		SideBarTab(int value){
			this.value = value;
		}
		int getValue(){
			return value;
		}
	}
	private SideBarTab currentTab = SideBarTab.STAGES;
	private int tabWidth, tab1X, tab2X, tab3X;
	
	/*
	 * Constructor for initializing all variables
	 * @param: int sideBarWidth -> width of sidebar
	 * @param int screenHeight -> Height of screen, so sidebar will be same height 
	 */
	public Sidebar(int sideBarWidth, int screenHeight, Terrain terrain){
		this.sideBarWidth = sideBarWidth;
		this.sideBarHeight = screenHeight;
		this.terrain = terrain;
		this.tabWidth = sideBarWidth/3;
		this.tab1X = 0;
		this.tab2X = sideBarWidth/3;
		this.tab3X = 2*sideBarWidth/3;
		loadDrawableObjects();
	}
	
	/*
	 * Add your new objects to this method. This method is run when the sidebar is drawn and will automatically align and place 
	 * all inserted objects. Only thing YOU have to do is insert nice parameter values to the object instantiation.
	 * 
	 * Important: only the scaling parameter matters, the x and y values will be overwritten by the system
	 */
	public void loadDrawableObjects(){
		//stages
		drawableStages.put("Stage1", new SimulatorStage(0, 0, 0.26, 1, terrain));
		drawableStages.put("Stage2",new SimulatorStage(0, 0, 0.26, 1, terrain));
		drawableStages.put("Stage3",new SimulatorStage(0, 0, 0.26, 1, terrain));
		drawableStages.put("Stage4", new SimulatorStage(0, 0, 0.26, 1, terrain));
		drawableStages.put("Stage5", new SimulatorStage(0, 0, 0.26, 1, terrain));
		drawableStages.put("Stage6", new SimulatorStage(0, 0, 0.26, 1, terrain));
		drawableStages.put("Stage7", new SimulatorStage(0, 0, 0.26, 1, terrain));
		drawableStages.put("Stage8", new SimulatorStage(0, 0, 0.26, 1, terrain));
		drawableStages.put("Stage9", new SimulatorStage(0, 0, 0.26, 1, terrain));
		drawableStages.put("Stage10", new SimulatorStage(0, 0, 0.26, 1, terrain));
		drawableStages.put("Stage11", new SimulatorStage(0, 0, 0.26, 1, terrain));
		
		//facilities
		drawableFacilities.put("Toilet", new RestRoom(0, 0, 0.8, 1, this.terrain));
		drawableFacilities.put("Ingang", new AccessPoint(terrain, 0, 0, 0.26, 1));
		drawableFacilities.put("Uitgang", new ExitPoint(terrain, 0, 0, 0.26, 1));
		drawableFacilities.put("Snackbar", new SnackBar(0, 0, 0.2, 1, this.terrain));
		drawableFacilities.put("Wegwijzer", new WayPoint(0, 0, 0.8, 1, this.terrain));
		
		//paths
		drawablePaths.put("Ground-stone", Images.ImageType.PathGroundStone);
		drawablePaths.put("Dirt", Images.ImageType.PathDirt);
		drawablePaths.put("Stone", Images.ImageType.PathStone);
		
		//ornaments
		drawableOrnaments.put("Tree1", new Tree1(0, 0, 0.7, 1, this.terrain));
		drawableOrnaments.put("PalmTree1", new PalmTree1(0, 0, 0.7, 1, this.terrain));
		drawableOrnaments.put("Fence", new Fence(0, 0, 0.4, 1, this.terrain));

		drawableArrays.add(drawableStages);
		drawableArrays.add(drawableFacilities);
		drawableArrays.add(drawableOrnaments);
	}
	
	public void draw(Graphics2D g2){
		//setclip
        g2.setClip(new Rectangle2D.Double(0,0, sideBarWidth, sideBarHeight));
        
        //header + tabs
        g2.setColor(Color.GRAY);
        //g2.fill3DRect(x, y, width, height, raised);
        g2.fill3DRect(0, 0, sideBarWidth, 30, true);
        g2.fill3DRect(tab1X, 30, tabWidth, 30, true);
        g2.fill3DRect(tab2X, 30, tabWidth, 30, true);
        g2.fill3DRect(tab3X, 30, tabWidth, 30, true);  
        g2.fill3DRect(0, 60, sideBarWidth, 30, true); 
        g2.setColor(Color.WHITE);
        g2.setFont(new Font("SansSerif", Font.BOLD, 20));
        //g2.drawString(str, x, y);
        g2.drawString("Drag and drop", 30, 23);  
        g2.setFont(new Font("SansSerif", Font.PLAIN, 16));
        g2.drawString("Stages", tab1X+10, 50);
        g2.drawString("Facilities", tab2X+3, 50);
        g2.drawString("Paths", tab3X+15, 50);
        g2.drawString("Ornaments", sideBarWidth/3, 80);
        switch (currentTab) {
		case STAGES:
			g2.drawLine(tab1X+10, 55, tab1X+60, 55);
			break;
		case FACILITIES:
			g2.drawLine(tab2X+3, 55, tab2X+64, 55);
			break;
		case PATHS:
			g2.drawLine(tab3X+15, 55, tab3X+55, 55);
			break;
		case ORNAMENTS:
			g2.drawLine(sideBarWidth/3, 85, (sideBarWidth/3)+80, 85);
			break;
		default:
			break;
		}
        
        //draw objects
        g2.setColor(Color.BLACK);
        int objectPlacementX = 0;
        int objectPlacementY = 91;

        if(currentTab == SideBarTab.PATHS){
        	for(Entry<String, ImageType> object : drawablePaths.entrySet()){
        		g2.drawImage(Images.getImage(object.getValue()), objectPlacementX +10, objectPlacementY +10, sideBarWidth/2 -20, 80, null);
        		g2.drawString(object.getKey(), objectPlacementX+2, objectPlacementY + 95);
				g2.drawRect(objectPlacementX, objectPlacementY, sideBarWidth/2, 100);
        		objectPlacementX += 100;
				//switch to next row
				if(objectPlacementX % 200 == 0){
					objectPlacementX = 0;
					objectPlacementY += 100;
				}
        	}
        }else{
			for(Entry<String, DrawEngine> object : drawableArrays.get(currentTab.getValue()).entrySet()){
				object.getValue().setX(objectPlacementX);
				object.getValue().setY(objectPlacementY);
				if(object.getValue().getFacilityName() == "Fence")
					object.getValue().setY(objectPlacementY+50);
				object.getValue().draw(g2);
				g2.drawString(object.getKey(), objectPlacementX+2, objectPlacementY + 95);
				g2.drawRect(objectPlacementX, objectPlacementY, sideBarWidth/2, 100);
				objectPlacementX += 100;
				//switch to next row
				if(objectPlacementX % 200 == 0){
					objectPlacementX = 0;
					objectPlacementY += 100;
				}
			}
        }
		//draw seperation line between sidebar and field
        g2.setColor(Color.BLACK);
        g2.setStroke(new BasicStroke(5));
        g2.drawLine(199, 0, 199, sideBarHeight);
    }
	
	/*
	 * Make new facility and place it on the mousepointer, dependent on where the user clicked in the sidebar
	 * @param: MouseEvent e -> Used to get mouselocation
	 */
    protected void getFacility(MouseEvent e){
    	Point2D clickPoint = terrain.getClickPoint(e.getPoint());
    	DrawEngine dragObject = null;
    	if(e.getX() < sideBarWidth)
		{
    		//check for click on tabs
    		if(e.getY() > 30 && e.getY() < 60){
    			if(e.getX() > tab1X && e.getX() < tab2X){
    				currentTab = SideBarTab.STAGES;
    			}else if(e.getX() > tab2X && e.getX() < tab3X){
    				currentTab = SideBarTab.FACILITIES;
    			}else if(e.getX() > tab3X && e.getX() < sideBarWidth){
    				currentTab = SideBarTab.PATHS;
    			}
    		}
    		if(e.getY() > 60 && e.getY() < 90){
    			currentTab = SideBarTab.ORNAMENTS;
    		}
    		//check for object selection
    		int objectPlacementX = 0;
            int objectPlacementY = 61;
    
            if(currentTab == SideBarTab.PATHS){
              	for(Entry<String, ImageType> object : drawablePaths.entrySet()){
	    			if(new Rectangle(objectPlacementX, objectPlacementY, sideBarWidth/2, 100).contains(e.getPoint())){
	    				terrain.newWalkingPath(object.getValue());
	    			}
	        		objectPlacementX += 100;
					//switch to next row
					if(objectPlacementX % 200 == 0){
						objectPlacementX = 0;
						objectPlacementY += 100;
					}
              	}
            }else{
            	for(Entry<String, DrawEngine> object : drawableArrays.get(currentTab.getValue()).entrySet()){
	    			if(object.getValue().contains(e.getPoint())){
	    				try {
							Constructor<?> constructor = object.getValue().getClass().getConstructor(int.class, int.class, double.class, double.class, Terrain.class, boolean.class);
							dragObject = (DrawEngine) constructor.newInstance(new Object[] { (int)clickPoint.getX(),(int)clickPoint.getY(), 0.5, 10, terrain, true});
							terrain.setDragObject(dragObject);					
	    				} catch (NoSuchMethodException e1) {
							try{
								Constructor<?> constructor = object.getValue().getClass().getConstructor(int.class, int.class, double.class, double.class, Terrain.class);
								dragObject = (DrawEngine) constructor.newInstance(new Object[] { (int)clickPoint.getX(),(int)clickPoint.getY(), 0.5, 10, terrain});
								terrain.setDragObject(dragObject);
							}catch (NoSuchMethodException ex){
								ex.printStackTrace();
							} catch (InvocationTargetException e2) {
								e2.printStackTrace();
							} catch (InstantiationException e2) {
								e2.printStackTrace();
							} catch (IllegalAccessException e2) {
								e2.printStackTrace();
							}

						} catch (SecurityException |  IllegalArgumentException | InstantiationException | IllegalAccessException | InvocationTargetException ex){
							ex.printStackTrace();
						}
	    			}
	    		}
            }
			if(dragObject != null)
				terrain.addEntity(dragObject);
		}
    }
	
	public int getSideBarWidth(){
		return sideBarWidth;
	}
}
