package ui.view.main.old;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;

import com.google.gson.JsonParseException;

import classes.AbstractCanvasObject;
import classes.Edge;
import classes.GroupNode;
import classes.HolonObject;
import classes.HolonSwitch;
import classes.Node;
import classes.Position;
import classes.Vector2d;
import model.Consumer;
import model.DecoratedCable;
import model.DecoratedGroupNode;
import model.DecoratedHolonObject;
import model.DecoratedSwitch;
import model.ExitCable;
import model.Model;
import model.Passiv;
import model.Supplier;
import model.DecoratedHolonObject.HolonObjectState;
import model.DecoratedSwitch.SwitchState;
import ui.controller.Control;
import ui.controller.UpdateController;
import ui.view.component.ButtonTabComponent;
import utility.ImageImport;

/**
 * This Class is the Canvas. All Objects will be visualized here
 *
 * @author Gruppe14
 */
public class GroupNodeCanvas extends AbstractCanvas implements MouseListener, MouseMotionListener {

    private static final long serialVersionUID = 1L;
    // The UpperNode
    public GroupNode upperNode;
    // Path
    public String path;
    public int code;
    public boolean disable = false;
    /**
     * Constructor.
     *
     * @param mod       the Model
     * @param control   the Controller
     * @param unitGraph
     */
    public GroupNodeCanvas(Model mod, Control control, UnitGraph unitGraph, GroupNode UpperNode, String parentPath, Component parentComponent) {
        toolTip = false;

        this.controller = control;
        this.model = mod;
        this.upperNode = UpperNode;
        this.code = UpperNode.hashCode();
        this.path = parentPath + upperNode.getName();
        scalediv20 = model.getScale() / 20;

        // Cps objecte aus dem border links schieben
        upperNode.setLeftBorder(/*(int) (50 + scalediv20 + scalediv20 + 10)*/ 0);
        for (AbstractCanvasObject cps : upperNode.getNodes()) {
            if (cps.getPosition().x < model.getScaleDiv2() + upperNode.getLeftBorder() + 5) {
                cps.setPosition(
                        new Position(upperNode.getLeftBorder() + 5 + model.getScaleDiv2(), cps.getPosition().y));
            }
        }

        showedInformation[0] = true;
        showedInformation[1] = true;
        showedInformation[4] = true;

        popmenu.add(itemCut);
        popmenu.add(itemCopy);
        popmenu.add(itemPaste);
        popmenu.add(itemDelete);
        popmenu.add(itemAlign);
        popmenu.addSeparator();
        popmenu.add(itemGroup);
        popmenu.add(itemUngroup);
        popmenu.add(itemCreateTemplate);
        
        itemAlign.setEnabled(true);
        itemDelete.setEnabled(false);
        itemCut.setEnabled(false);
        itemCopy.setEnabled(false);
        itemPaste.setEnabled(true);
        itemGroup.setEnabled(false);
        itemUngroup.setEnabled(false);
        
        updCon = new UpdateController(model, controller);
        
        itemAlign.addActionListener(actionEvent -> {
        	for(AbstractCanvasObject cps: model.getSelectedCpsObjects())
        		align(cps, model.getScaleDiv2());
        	repaint();
        });
        
        itemGroup.addActionListener(actionEvent -> {
            // calculate uppernode pos (taken from the controller)
            unPos = new Position(0, 0);
            animCps = new ArrayList<>();
            for (AbstractCanvasObject cps : model.getSelectedCpsObjects()) {
                animCps.add(cps); // add to animation Cps ArrayList
                unPos.x += cps.getPosition().x;
                unPos.y += cps.getPosition().y;
            }
            unPos.x /= animCps.size();
            unPos.y /= animCps.size();

            // save old Position
            savePos = new ArrayList<>();
            for (int i = 0; i < animCps.size(); i++) {
                savePos.add(new Position(0, 0));
                savePos.get(i).x = animCps.get(i).getPosition().x;
                savePos.get(i).y = animCps.get(i).getPosition().y;
            }

            animT = new javax.swing.Timer(animDelay, actionEvent1 -> {
                if (animDuration - animDelay > 0 && animCps.size() > 1) {
                    for (AbstractCanvasObject currentAnimCps : animCps) {
                        double x1 = currentAnimCps.getPosition().x - unPos.x;
                        double y1 = currentAnimCps.getPosition().y - unPos.y;
                        currentAnimCps.getPosition().x -= x1 / animSteps;
                        currentAnimCps.getPosition().y -= y1 / animSteps;
                    }
                    repaint();
                    animDuration -= animDelay;
                    animSteps--;
                } else {
                    animDuration = ANIMTIME;
                    animSteps = animDuration / animDelay;
                    animT.stop();
                    for (int i = 0; i < animCps.size(); i++) {
                        animCps.get(i).getPosition().x = savePos.get(i).x;
                        animCps.get(i).getPosition().y = savePos.get(i).y;
                    }
                    controller.addUpperNode("GroupNode", upperNode, model.getSelectedCpsObjects());
                    controller.calculateStateAndVisualForCurrentTimeStep();
                    repaint();
                }
            });
            animT.start();
        });

        itemUngroup.addActionListener(actionEvent -> {
            // save old Position
        	closeUpperNodeTab(tempCps.getId());

            savePos = new ArrayList<>();
            animCps = ((GroupNode) tempCps).getNodes();
            controller.ungroupGroupNode((GroupNode) tempCps, upperNode);

            for (int i = 0; i < animCps.size(); i++) {
                savePos.add(new Position(0, 0));
                savePos.get(i).x = animCps.get(i).getPosition().x;
                savePos.get(i).y = animCps.get(i).getPosition().y;
            }
            for (AbstractCanvasObject cps : animCps) {
                int x = tempCps.getPosition().x;
                int y = tempCps.getPosition().y;

                cps.setPosition(new Position(x, y));
            }

            animT = new javax.swing.Timer(animDelay, actionEvent1 -> {
                if (animDuration - animDelay >= 0) {
                    for (int i = 0; i < animCps.size(); i++) {
                        double x1 = animCps.get(i).getPosition().x - savePos.get(i).x;
                        double y1 = animCps.get(i).getPosition().y - savePos.get(i).y;
                        animCps.get(i).getPosition().x -= x1 / animSteps;
                        animCps.get(i).getPosition().y -= y1 / animSteps;
                    }
                    repaint();
                    animDuration -= animDelay;
                    animSteps--;
                } else {
                    animDuration = ANIMTIME;
                    animSteps = animDuration / animDelay;
                    animT.stop();
                    for (int i = 0; i < animCps.size(); i++) {
                        animCps.get(i).getPosition().x = savePos.get(i).x;
                        animCps.get(i).getPosition().y = savePos.get(i).y;
                    }

                    controller.calculateStateAndVisualForCurrentTimeStep();
                    repaint();
                }
            });
            animT.start();
        });

        

        itemDelete.addActionListener(actionEvent -> {
            // Remove the selected Object objects
            for (AbstractCanvasObject cps : model.getSelectedCpsObjects()) {
                if (upperNode.getNodes().contains(cps)) {
                    controller.delObjUpperNode(cps, upperNode);
                    // Remove UpperNodeTab if UpperNode deleted
                    if (cps instanceof GroupNode) {
                        JTabbedPane tabbedPane = (JTabbedPane) getParent().getParent().getParent().getParent();
                        // Look if the uppernode is open in a Tab
                        for (int i = 4; i < tabbedPane.getTabCount(); i++) {

                            if (tabbedPane.getComponentAt(i) != null &&
                                    ((GroupNodeCanvas) ((JScrollPane) tabbedPane.getComponentAt(i)).getViewport()
                                            .getComponent(0)).upperNode.getId() == cps.getId()) {
                                ((ButtonTabComponent) tabbedPane.getTabComponentAt(i)).removeTabs();
                                break;
                            }
                        }
                      
                    }
                }
            }
            toolTip = false;
            model.getSelectedCpsObjects().clear();
            tempCps = null;
            repaint();
        });

        itemCut.addActionListener(actionEvent -> {
            controller.cut(upperNode);
            itemPaste.setEnabled(true);
            repaint();
        });

        itemCopy.addActionListener(actionEvent -> {
        	if(tempCps instanceof GroupNode)
				controller.getObjectsInDepth();
            controller.copy(upperNode);
            itemPaste.setEnabled(true);
            repaint();
        });

        itemPaste.addActionListener(actionEvent -> {
            try {
                controller.paste(upperNode, mousePosition);
            } catch (JsonParseException | UnsupportedFlavorException | IOException e1) {
                JLabel message = new JLabel("The Clipboard information cannot be pastet into Application.");
                JOptionPane.showMessageDialog(null, message, "", JOptionPane.ERROR_MESSAGE);
            }
            repaint();
        });
        
        /*
		 * create Template
		 */
		itemCreateTemplate.addActionListener(actionEvent -> {
			controller.createTemplate((HolonObject)tempCps, (JFrame)SwingUtilities.getRoot(this));
		});

        this.addMouseListener(this);
        this.addMouseMotionListener(this);
    }

	/**
	 * Paints all Components on the Canvas.
	 *
	 * @param g
	 *            Graphics
	 */
	
	private Color getStateColor(HolonObjectState state) {
		switch(state) {
		case NOT_SUPPLIED:
			return new Color(230, 120, 100);
		case NO_ENERGY:
			return Color.white;
		case OVER_SUPPLIED:
			return new Color(166, 78, 229);
		case PARTIALLY_SUPPLIED:
			return Color.yellow;
		case PRODUCER:
			return Color.lightGray;
		case SUPPLIED:
			return new Color(13, 175, 28);
		default:
			return Color.BLACK;
		}
	}
	private void paintCanvasObject(Graphics2D g, DecoratedHolonObject decoratedHolonObject){
		Position pos = decoratedHolonObject.getModel().getPosition();
		Color statecolor = getStateColor(decoratedHolonObject.getState());
		g.setColor(statecolor);
		g.fillRect(pos.x - controller.getScaleDiv2(), pos.y - controller.getScaleDiv2(), controller.getScale(), controller.getScale());
		drawCanvasObject(g, decoratedHolonObject.getModel().getImage(), pos);
	}
	private void drawCanvasObjectString(Graphics2D g, Position posOfCanvasObject, float energy) {
		g.setColor(Color.BLACK);
		g.setFont(new Font("TimesNewRoman", Font.PLAIN, (int) (controller.getScale() / 4f) )); 
		g.drawString((energy > 0)? "+" + Float.toString(energy): Float.toString(energy), posOfCanvasObject.x - controller.getScaleDiv2(), posOfCanvasObject.y - controller.getScaleDiv2() - 1);
	}
	private void paintConsumer(Graphics2D g, Consumer con){
		paintCanvasObject(g, con);
		paintSupplyBar(g,con.getSupplyBarPercentage(), getStateColor(con.getState()), con.getModel().getPosition());
		drawCanvasObjectString(g, con.getModel().getPosition(), -con.getEnergyNeededFromNetwork());
	}
	private void paintSupplier(Graphics2D g, Supplier sup){
		paintCanvasObject(g, sup);
		drawCanvasObjectString(g, sup.getModel().getPosition(), sup.getEnergyToSupplyNetwork());
	}
	
	private void drawCanvasObject(Graphics2D g, String Image, Position pos) {
		g.drawImage(ImageImport.loadImage(Image, controller.getScale(), controller.getScale()) , 
				pos.x - controller.getScaleDiv2(),
				pos.y - controller.getScaleDiv2(),
				controller.getScale(), controller.getScale() , null);
	}
	
	private void paintCable(Graphics2D g, DecoratedCable cable, boolean isSelected)
	{
		Position start = cable.getModel().getA().getPosition();
		Position end =  cable.getModel().getB().getPosition();
		float currentEnergy = cable.getFlowEnergy();
		float capacity = cable.getModel().getCapacity();
		boolean unlimited = cable.getModel().isUnlimitedCapacity();
		switch(cable.getState()) {
		case Burned:
			g.setColor(Color.RED);
			g.setStroke(new BasicStroke(2));
			break;
		case Working:
			g.setColor(new Color(13, 175, 28));
			g.setStroke(new BasicStroke(unlimited?2f:(currentEnergy / capacity * 2f) + 1));
			break;
		}
		if(isSelected){
			g.setColor(Color.lightGray);
		}
		g.drawLine(start.x, start.y, end.x, end.y);
		Position middle = new Position((start.x + end.x) / 2, (start.y + end.y) / 2);
		g.setFont(new Font("TimesRoman", Font.PLAIN, Math.max((int) (controller.getScale() / 3.5f), 10) )); 
		g.drawString(currentEnergy + "/" + (unlimited?"\u221E":capacity) , middle.x, middle.y);
	}
	private void paintSwitch(Graphics2D g, DecoratedSwitch dSwitch)
	{
		drawCanvasObject(g, dSwitch.getState() == SwitchState.Open ? HolonSwitch.getSwitchOpenImage(): HolonSwitch.getSwitchClosedImage() , dSwitch.getModel().getPosition());
	}
	private void paintGroupNode(Graphics2D g, DecoratedGroupNode dGroupNode) {
		Position pos = dGroupNode.getModel().getPosition();
		g.setColor(Color.lightGray);
		g.fillRect(pos.x - controller.getScaleDiv2(), pos.y - controller.getScaleDiv2(), controller.getScale(), controller.getScale());
		drawCanvasObject(g, "/Images/upper_node.png" , pos);
		paintGroupNodeBar(g, dGroupNode, pos);
	}
	private void paintGroupNodeBar(Graphics2D g, DecoratedGroupNode dGroupNode , Position pos) {
		// +1, -2, -1 little Adjustment for pixel perfect alignment
		int barWidth = (int) (controller.getScale());
		int barHeight = (int) (controller.getScale() / 5);
		g.setColor(Color.WHITE);
		g.fillRect(pos.x - controller.getScaleDiv2(), pos.y + controller.getScaleDiv2() - 1, (int) barWidth, barHeight);
		float[] percentages = getGroupNodeBarPercentages(dGroupNode);
		Color[] colors = new Color[6];
		colors[0] = getStateColor(HolonObjectState.PRODUCER);
		colors[1] = getStateColor(HolonObjectState.NOT_SUPPLIED);
		colors[2] = getStateColor(HolonObjectState.PARTIALLY_SUPPLIED);
		colors[3] = getStateColor(HolonObjectState.SUPPLIED);
		colors[4] = getStateColor(HolonObjectState.OVER_SUPPLIED);
		colors[5] = getStateColor(HolonObjectState.NO_ENERGY);
				
		for(int i = 5; i>=0; i--) {
			g.setColor(colors[i]);
			g.fillRect(pos.x - controller.getScaleDiv2(), pos.y + controller.getScaleDiv2() - 1, (int) (barWidth * percentages[i] - 1), barHeight);		
		}
//		g.setColor(color);
//		g.fillRect(pos.x - controller.getScaleDiv2(), pos.y + controller.getScaleDiv2() - 1, (int) (barWidth * (percentage < 1 ? percentage : 1.0f) - 1), barHeight);
		g.setColor(Color.BLACK);
		g.setStroke(new BasicStroke(1));
		g.drawRect(pos.x - controller.getScaleDiv2(), pos.y + controller.getScaleDiv2() - 1, barWidth - 1 , barHeight);
	}
	/**
	 * HardCoded Stuff dont try at Home ;)
	 * @param dGroupNode
	 * @return
	 */
	public float[] getGroupNodeBarPercentages(DecoratedGroupNode dGroupNode) {
		int[] amountOfObjects = new int[6];
		amountOfObjects[0] = dGroupNode.getAmountOfSupplier();
		amountOfObjects[1] = dGroupNode.getAmountOfConsumerWithState(HolonObjectState.NOT_SUPPLIED);
		amountOfObjects[2] = dGroupNode.getAmountOfConsumerWithState(HolonObjectState.PARTIALLY_SUPPLIED);
		amountOfObjects[3] = dGroupNode.getAmountOfConsumerWithState(HolonObjectState.SUPPLIED);
		amountOfObjects[4] = dGroupNode.getAmountOfConsumerWithState(HolonObjectState.OVER_SUPPLIED);
		amountOfObjects[5] = dGroupNode.getAmountOfPassiv();
		int countHolonObjects = amountOfObjects[0] + 	amountOfObjects[1] + amountOfObjects[2] + amountOfObjects[3] + amountOfObjects[4] + amountOfObjects[5];
		float[] percentages = new float[6];
		int count = 0;
		for(int i = 0; i < 6; i++) {
			count += amountOfObjects[i];
			percentages[i] = (float)count / (float)countHolonObjects;
		}
		return percentages;
	}
	private void paintExitCable(Graphics2D g, ExitCable eCable) {
		if(eCable.getStart() == null || eCable.getFinish() == null) {
			System.out.println("Null wrong exitcable");
			System.out.println(" wrongCable: " + " start:"  +eCable.getStart() + " end:"  +eCable.getFinish() + " state:" + eCable.getState());
			return;
		}
		Position start = eCable.getStart().getPosition();
		Position end = eCable.getFinish().getPosition();
		float currentEnergy = eCable.getCable().getFlowEnergy();
		float capacity = eCable.getCable().getModel().getCapacity();
		boolean unlimited = eCable.getCable().getModel().isUnlimitedCapacity();
		switch(eCable.getCable().getState()) {
		case Burned:
			g.setColor(Color.RED);
			g.setStroke(new BasicStroke(2));
			break;
		case Working:
			g.setColor(new Color(13, 175, 28));
			g.setStroke(new BasicStroke(unlimited?2f:(currentEnergy / capacity * 2f) + 1));
			break;
		}
		switch(eCable.getState()) {
		case DOWN:
		case DOWNDOWN:
			g.drawLine(start.x, start.y, end.x, end.y);
			Position middle = new Position((start.x + end.x) / 2, (start.y + end.y) / 2);
			g.setFont(new Font("TimesRoman", Font.PLAIN, Math.max((int) (controller.getScale() / 3.5f), 10) )); 
			g.drawString(currentEnergy + "/" + (unlimited?"\u221E":capacity) , middle.x, middle.y);
			break;
		case DOWNUP:
		case UP:
			Vector2d vStart = new Vector2d(start.x, start.y);
			Vector2d vEnd = new Vector2d(end.x, end.y);
			float stretchFactor = 4 * model.getScale();
			stretchFactor =  (stretchFactor > vStart.getDistance(vEnd))? vStart.getDistance(vEnd) : stretchFactor;
			Vector2d vPosition = vStart.add((vEnd.subtract(vStart)).normalize().multiply(stretchFactor));
			Position result = new Position(Math.round(vPosition.getX()),Math.round(vPosition.getY()));
			g.drawLine(start.x, start.y, result.x, result.y);
			Position middle1 = new Position((start.x +result.x) / 2, (start.y + +result.y) / 2);
			g.setFont(new Font("TimesRoman", Font.PLAIN, Math.max((int) (controller.getScale() / 3.5f), 10) )); 
			g.drawString(currentEnergy + "/" + (unlimited?"\u221E":capacity) , middle1.x, middle1.y);
			drawCanvasObject(g, "/Images/arrowUp.png" , result);
			break;
		default:
			System.out.println("Error");
			break;
		}
	}
	private void paintSupplyBar(Graphics2D g, float percentage, Color color, Position pos) {
		// +1, -2, -1 little Adjustment for pixel perfect alignment
		int barWidth = (int) (controller.getScale());
		int barHeight = (int) (controller.getScale() / 5);
		g.setColor(Color.WHITE);
		g.fillRect(pos.x - controller.getScaleDiv2(), pos.y + controller.getScaleDiv2() - 1, (int) barWidth, barHeight);
		g.setColor(color);
		g.fillRect(pos.x - controller.getScaleDiv2(), pos.y + controller.getScaleDiv2() - 1, (int) (barWidth * (percentage < 1 ? percentage : 1.0f) - 1), barHeight);
		g.setColor(Color.BLACK);
		g.setStroke(new BasicStroke(1));
		g.drawRect(pos.x - controller.getScaleDiv2(), pos.y + controller.getScaleDiv2() - 1, barWidth - 1 , barHeight);
		g.setFont(new Font("TimesNewRoman", Font.PLAIN, (int) (barHeight * 1.5) - 2)); 
		String percentageString = (Math.round((percentage * 100))) + "%";
		int stringWidth = (int) g.getFontMetrics().getStringBounds(percentageString, g).getWidth();
		if(percentage > 1.0f) g.setColor(Color.WHITE); //Just to see better on purple
		g.drawString(percentageString, pos.x + 1 - stringWidth / 2, pos.y + controller.getScaleDiv2() - 1+ barHeight);
		
	}
	
	//old code
	void drawMarker(Graphics2D g) {
		Color transparentGrey = new Color(128, 174, 247, 40);
		if (sx > x && sy > y) {
			g.drawRect(x, y, sx - x, sy - y);
			g.setColor(transparentGrey);
			g.fillRect(x, y, sx - x, sy - y);
		} else if (sx < x && sy < y) {
			g.drawRect(sx, sy, x - sx, y - sy);
			g.setColor(transparentGrey);
			g.fillRect(sx, sy, x - sx, y - sy);
		} else if (sx >= x) {
			g.drawRect(x, sy, sx - x, y - sy);
			g.setColor(transparentGrey);
			g.fillRect(x, sy, sx - x, y - sy);
		} else if (sy >= y) {
			g.drawRect(sx, y, x - sx, sy - y);
			g.setColor(transparentGrey);
			g.fillRect(sx, y, x - sx, sy - y);
		}
	}
	public void paintComponent(Graphics g) {		
		super.paintComponent(g);
		Graphics2D g2d = (Graphics2D) g;
		g2d.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON));
		//-->Old code
		if (drawEdge) {
			g2d.setColor(Color.BLACK);
			g2d.setStroke(new BasicStroke(1));
			g2d.drawLine(tempCps.getPosition().x, tempCps.getPosition().y, x, y);
		}
		//<--
		//SelectedCable
		HashSet<Edge> selectedEdges = new HashSet<Edge>();
		for(AbstractCanvasObject aCps:  model.getSelectedCpsObjects()) {
			for(Edge edge: aCps.getConnections()) {
				selectedEdges.add(edge);
			}
		}
		if(model.getSelectedEdge() != null) selectedEdges.add(model.getSelectedEdge());

		
		DecoratedGroupNode  actualGroupNode = controller.getSimManager().getActualVisualRepresentationalState().getCreatedGroupNodes().get(upperNode);
		//VisualState Representation:
		for(ExitCable cable : actualGroupNode.getExitCableList()) {
			paintExitCable(g2d, cable);
		}
		for(DecoratedCable cable : actualGroupNode.getInternCableList()) {
			paintCable(g2d, cable, selectedEdges.contains(cable.getModel()));
		}
	
		
		for(DecoratedGroupNode dGroupNode : actualGroupNode.getGroupNodeList()) {
			paintGroupNode(g2d, dGroupNode);
		}
		for(Consumer con: actualGroupNode.getConsumerList()) {
			paintConsumer(g2d, con);					
		}
		for(Supplier sup: actualGroupNode.getSupplierList()) {
			paintSupplier(g2d, sup);				
		}
		for(Passiv pas: actualGroupNode.getPassivList()) {
			paintCanvasObject(g2d, pas);
		}
		for(DecoratedSwitch dSwitch : actualGroupNode.getSwitchList()) {
				paintSwitch(g2d, dSwitch);
		}
		for(Node node : actualGroupNode.getNodeList()) {
			drawCanvasObject(g2d, "/Images/node.png" , node.getPosition());
		}
		
		//-->oldCode 
		if (doMark) {
			g2d.setColor(Color.BLACK);
			g2d.setStroke(new BasicStroke(0));
			drawMarker(g2d);
		}
		//Test Selection
		//Objects:
		g2d.setColor(Color.BLUE);
		g2d.setStroke(new BasicStroke(1));
		Color transparentGrey = new Color(128, 174, 247, 40);
		for(AbstractCanvasObject aCps:  model.getSelectedCpsObjects()) {
			if(aCps instanceof Node) {
				Position pos = aCps.getPosition();
				g2d.setColor(transparentGrey);
				g2d.fillOval(pos.x - (int) (controller.getScaleDiv2()), pos.y - (int) (controller.getScaleDiv2()),  controller.getScale(),  controller.getScale());
				g2d.setColor(Color.LIGHT_GRAY);
				g2d.setStroke(new BasicStroke(2));
				g2d.drawOval(pos.x - (int) (controller.getScaleDiv2()), pos.y - (int) (controller.getScaleDiv2()),  controller.getScale(),  controller.getScale());
			}
			else {
				Position pos = aCps.getPosition();
				g2d.setColor(transparentGrey);
				g2d.fillRect(pos.x - (int) (controller.getScaleDiv2()* 1.5f), pos.y - (int) (controller.getScaleDiv2()* 1.5f), (int) (controller.getScale()* 1.5f) , (int) (controller.getScale()* 1.5f));
				g2d.setColor(Color.LIGHT_GRAY);
				g2d.setStroke(new BasicStroke(2));
				g2d.drawRect(pos.x - (int) (controller.getScaleDiv2()* 1.5f), pos.y - (int) (controller.getScaleDiv2()* 1.5f), (int) (controller.getScale()* 1.5f) , (int) (controller.getScale()* 1.5f));				
			}

		}
		//maybeReplace:
		if(mayBeReplaced != null){
			g2d.setColor(Color.RED);
			Position pos = mayBeReplaced.getPosition();
			g.drawImage(ImageImport.loadImage("/Images/replace.png") , 
					pos.x + controller.getScaleDiv2(),
					pos.y - controller.getScale(),
					controller.getScaleDiv2(), controller.getScaleDiv2(), null);
		}
		//<-- OldCode
	}
   

    @Override
    public void mouseClicked(MouseEvent e) {
    	if(!disable){
        triggerUpdateController();
        stopEditing();
    	}
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
    	if(!disable){
    	stopEditing();
        tempCps = null;
        dataSelected = null;
        edgeHighlight = null;
        controller.setSelecteEdge(null);
        controller.setSelectedObjectID(-1);
        // Object Selection

        // Erase old data in the PropertyTable
        if (model.getPropertyTable().getRowCount() > 0) {
            for (int i = model.getPropertyTable().getRowCount() - 1; i > -1; i--) {
                model.getPropertyTable().removeRow(i);
            }
        }

        if (e.getX() > upperNode.getLeftBorder()) {
            for (AbstractCanvasObject cps : upperNode.getNodes()) {
                cx = cps.getPosition().x - model.getScaleDiv2();
                cy = cps.getPosition().y - model.getScaleDiv2();
                if (x - controller.getScale() <= cx && y - controller.getScale() <= cy && x >= cx && y >= cy) {
                    tempCps = cps;


                    dragging = true;
                    if (e.isControlDown() && tempCps != null) {
                        if (model.getSelectedCpsObjects().contains(tempCps)) {
                            controller.deleteSelectedObject(tempCps);
                        } else {
                            controller.addSelectedObject(tempCps);
                            if(tempCps instanceof GroupNode)
    							controller.getObjectsInDepth();
                        }
                    }

                    // If drawing an Edge (CTRL down)
                    if (tempCps.getClass() == HolonObject.class) {
                        HolonObject tempObj = ((HolonObject) tempCps);
                        dataSelected = tempObj.getElements();
                    }
                    if (e.isShiftDown()) {
                        drawEdge = true;
                        dragging = false;
                    }
                    break;
                }
            }
        } else {
            // look for objects connected to uppernode
            int count = 0;
            for (Edge ed : upperNode.getConnections()) {
                AbstractCanvasObject cps;
                if (ed.getA().equals(this.upperNode)) {
                    cps = ed.getB();
                } else {
                    cps = ed.getA();
                }
                if (x - controller.getScale() <= ((upperNode.getLeftBorder() >> 1) - model.getScaleDiv2())
                        && y - controller.getScale() <= (scalediv20 + 5 + (model.getScale() + scalediv20 + 10) * count)
                        && x >= (upperNode.getLeftBorder() >> 1) - model.getScaleDiv2()
                        && y >= (scalediv20 + 5 + (model.getScale() + scalediv20 + 10) * count)) {
                    tempCps = cps;
                    // If drawing an Edge (CTRL down)
                    if (tempCps.getClass() == HolonObject.class) {
                        HolonObject tempObj = ((HolonObject) tempCps);
                        dataSelected = tempObj.getElements();
                    }
                    if (e.isShiftDown()) {
                        drawEdge = true;
                    }
                }
                count++;
            }
        }

        // Selection of CpsObject
        // model.setSelectedCpsObject(tempCps);

        // Edge Selection
        if (e.getButton() == MouseEvent.BUTTON1) {
            if (tempCps == null) {
                edgeHighlight = mousePositionOnEdge(x, y);
                controller.setSelecteEdge(edgeHighlight);
                controller.setSelectedObjectID(0);
                if (!e.isControlDown() && e.getButton() != MouseEvent.BUTTON3) {
                    model.getSelectedCpsObjects().clear();
                }
                updCon.deleteRows(model.getMultiTable());
                updCon.deleteRows(model.getSingleTable());
            }

            if (edgeHighlight == null && tempCps == null) {
                sx = e.getX();
                sy = e.getY();
                doMark = true;
            }
            // System.out.println("Selected Objects");
            // for (AbstractCpsObject temp : model.getSelectedCpsObjects()) {
            // System.out.println(temp.getName() + " " + temp.getID());
            // }
            repaint();
        }
    	}
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    	if(!disable){
        x = e.getX();
        y = e.getY();
        dragging = false;
        if (model.getSelectedCpsObjects().size() > 1) {
            model.getTableHolonElement().setModel(model.getMultiTable());
        } else if (model.getSelectedCpsObjects().size() == 1) {
            model.getTableHolonElement().setModel(model.getSingleTable());
        }

        if (drawEdge) {
            drawEdge = false;
            drawDeleteEdge();
        }

        if (dragged) {
            try { 
            	/**
            	 * Save State before performing NodePlacement, replacement e.g.
            	 */
                controller.autoSave();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            
            /**
             * check if tempCps could replace an Object on the UpperNodeanvas
             */
            if(model.getSelectedCpsObjects().size() == 1 && checkForReplacement(upperNode.getNodes(), tempCps, tempCps.getPosition().x, tempCps.getPosition().y)){
            	/**
            	 * if UpperNode would be replaced, close its tabs
            	 */
            	if(mayBeReplaced instanceof GroupNode)
            		closeUpperNodeTab(mayBeReplaced.getId());
            	/**
            	 * replace on canvas
            	 */
            	controller.replaceObjUpperNode(mayBeReplaced, tempCps, upperNode);
            	mayBeReplaced=null;
            }
        }

        if (!e.isControlDown() && !dragged && tempCps != null && MouseEvent.BUTTON3 != e.getButton()) {
            model.getSelectedCpsObjects().clear();
            controller.addSelectedObject(tempCps);
            if(tempCps instanceof GroupNode)
				controller.getObjectsInDepth();
        }

        dragged = false;

        // Rightclick List
        setRightClickMenu(e);

        markObjects();

        if (doubleClick() && tempCps != null && tempCps instanceof HolonSwitch) {
            ((HolonSwitch) tempCps).switchState();
        }

        controller.calculateStateAndVisualForTimeStep(model.getCurIteration());
        triggerUpdateController();
        repaint();
    	}
    }

    @Override
    public void mouseDragged(MouseEvent e) {
    	if(!disable){
        // If Edge is drawn
        x = e.getX();
        y = e.getY();
        if (!model.getSelectedCpsObjects().contains(tempCps) && !doMark) {
            model.getSelectedCpsObjects().clear();
            if (tempCps != null) {
                controller.addSelectedObject(tempCps);
            }
        }
        if (dragging) {
            try {            	
                // tempCps in the upperNode? else its a connected Object from
                // outside
                if (upperNode.getNodes().contains(tempCps)) {
                    dragged = true;
                    float xDist, yDist; // Distance

                    x = e.getX();
                    y = e.getY();

                    // Make sure its in bounds
                    if (e.getX() < controller.getScaleDiv2() + upperNode.getLeftBorder() + 5)
                        x = controller.getScaleDiv2() + upperNode.getLeftBorder() + 5;
                    else if (e.getX() > this.getWidth() - controller.getScaleDiv2())
                        x = this.getWidth() - controller.getScaleDiv2();
                    if (e.getY() < controller.getScaleDiv2())
                        y = controller.getScaleDiv2();
                    else if (e.getY() > this.getHeight() - controller.getScaleDiv2())
                        y = this.getHeight() - controller.getScaleDiv2();

                    // Distance
                    xDist = x - tempCps.getPosition().x;
                    yDist = y - tempCps.getPosition().y;

                    tempCps.setPosition(x, y); // Drag Position

                    // TipText Position and name
                    toolTip = true;
                    toolTipText = tempCps.getName() + ", " + tempCps.getId();
                    toolTipPos.x = tempCps.getPosition().x - model.getScaleDiv2();
                    toolTipPos.y = tempCps.getPosition().y + model.getScaleDiv2();

                    // All Selected Objects
                    for (AbstractCanvasObject cps : model.getSelectedCpsObjects()) {
                        if (cps != tempCps) {
                            x = (int) (cps.getPosition().x + xDist);
                            y = (int) (cps.getPosition().y + yDist);

                            // Make sure its in bounds
                            if (x < upperNode.getLeftBorder() + 5 + controller.getScaleDiv2())
                                x = controller.getScaleDiv2() + upperNode.getLeftBorder() + 5;
                            else if (x > this.getWidth() - controller.getScaleDiv2())
                                x = this.getWidth() - controller.getScaleDiv2();
                            if (y <= controller.getScaleDiv2())
                                y = controller.getScaleDiv2();
                            else if (y > this.getHeight() - controller.getScaleDiv2())
                                y = this.getHeight() - controller.getScaleDiv2();

                            cps.setPosition(x, y);
                        }
                    }
                }
                /**
            	 * check if something would be replaced
            	 */
                if(model.getSelectedCpsObjects().size()==1)
                	checkForReplacement(upperNode.getNodes(), tempCps, x, y);

               repaint();
            } catch (Exception eex) {

            }
        	
        }

        // Mark Objects
        if (doMark) {
            tempSelected.clear();
            for (AbstractCanvasObject cps : upperNode.getNodes()) {
                int x1 = sx, x2 = x, y1 = sy, y2 = y;

                if (sx >= x) {
                    x1 = x;
                    x2 = sx;
                }
                if (sy >= y) {
                    y1 = y;
                    y2 = sy;
                }
                if (x1 <= cps.getPosition().x + model.getScaleDiv2() && y1 <= cps.getPosition().y + model.getScaleDiv2()
                        && x2 >= cps.getPosition().x && y2 >= cps.getPosition().y) {
                    tempSelected.add(cps);

                }
            }
            int count = 0;
            for (Edge ed : upperNode.getConnections()) {
                AbstractCanvasObject cps;
                if (ed.getA().equals(upperNode)) {
                    cps = ed.getB();
                } else {
                    cps = ed.getA();
                }

                int x1 = sx, x2 = x, y1 = sy, y2 = y;

                if (sx >= x) {
                    x1 = x;
                    x2 = sx;
                }
                if (sy >= y) {
                    y1 = y;
                    y2 = sy;
                }

                if (x1 <= upperNode.getLeftBorder() >> 1
                        && y1 <= (int) (5 + (model.getScale() + scalediv20 + 10) * count) + model.getScaleDiv2()
                        && x2 >= upperNode.getLeftBorder() >> 1
                        && y2 >= (int) (5 + (model.getScale() + scalediv20 + 10) * count)) {
                    tempSelected.add(cps);

                }
                count++;
            }
        }

        repaint();
    	}
    }

    @Override
    public void mouseMoved(MouseEvent e) {
    	{
        x = e.getX();
        y = e.getY();
        // Everything for the tooltip :)
        boolean on = false;
        for (AbstractCanvasObject cps : upperNode.getNodes()) {

            cx = cps.getPosition().x - controller.getScaleDiv2();
            cy = cps.getPosition().y - controller.getScaleDiv2();

            on = setToolTipInfoAndPosition(on, cps);
        }
        int count = 0;
        for (Edge ed : upperNode.getConnections()) {

            AbstractCanvasObject cps;
            if (ed.getA().equals(this.upperNode)) {
                cps = ed.getB();
            } else {
                cps = ed.getA();
            }

            cx = upperNode.getLeftBorder() >> 1;
            cy = (int) (scalediv20 + 5 + (50 + scalediv20 + 10) * count);
            if (x - 50 <= cx && y - 50 <= cy && x >= cx && y >= cy) {

                on = true;
                toolTipPos.x = cx - 25;
                toolTipPos.y = cy + 50;
                toolTipText = cps.getName() + ", " + cps.getId();

            }
            count++;
        }

        toolTip = on;
        repaint();
    	}
    }

    /**
     * Draws or Deletes an Edge.
     */
    void drawDeleteEdge() {
        boolean node = true; // new node?
        boolean newEdge = true;
        boolean onEdge = true;
        boolean deleteNode = false;
        boolean outsideCon = !upperNode.getNodes().contains(tempCps); // Connection to the outside
        boolean found = false; // dont search for outside connetion if inside
        // connection is found
        Edge e = null;
        for (AbstractCanvasObject cps : upperNode.getNodes()) {

            cx = cps.getPosition().x - controller.getScaleDiv2();
            cy = cps.getPosition().y - controller.getScaleDiv2();

            if (x - controller.getScale() <= cx && y - controller.getScale() <= cy && x >= cx && y >= cy
                    && cps != tempCps) {
                found = true;
                node = false;
                onEdge = false;
                for (Edge p : tempCps.getConnections()) {
                    if ((p.getA() == tempCps && p.getB() == cps) || (p.getB() == tempCps && p.getA() == cps)) {
                        newEdge = false;
                        e = p;
                    }
                }
                if (!newEdge) {
                    if (outsideCon) {
                    } else {
                        controller.removeEdgesOnCanvas(e);
                    }
                    // Node ohne Edge?
                    if (e.getA().getClass() == Node.class && e.getA().getConnections().isEmpty()) {
                        tempCps = e.getA();
                        deleteNode = true;
                    }
                    if (e.getB().getClass() == Node.class && e.getB().getConnections().isEmpty()) {
                        deleteNode = true;
                    }
                } else {
                	if(!(cps instanceof GroupNode || tempCps instanceof GroupNode)) {
						e = new Edge(cps, tempCps, model.getMaxCapacity());
						controller.addEdgeOnCanvas(e);
					}else if (cps instanceof GroupNode && !(tempCps instanceof GroupNode)){
						GroupNode thisUpperNode = (GroupNode)cps;
						Object[] possibilities = thisUpperNode.getNodes().stream().map(aCps -> new ACpsHandle(aCps)).filter(aCpsHandle -> !(aCpsHandle.object instanceof GroupNode)).toArray();
						if(possibilities.length != 0) {
							ACpsHandle selected = (ACpsHandle) JOptionPane.showInputDialog(this, "Select a inside Object:", "Connection To?",  JOptionPane.OK_OPTION,new ImageIcon(new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)) , possibilities, "");
							if(selected != null) {
								e = new Edge(selected.object, tempCps, model.getMaxCapacity());
								controller.addEdgeOnCanvas(e);
							}
						}else {
							Node n = new Node("Node");

							n.setPosition(x, y);
							controller.addObjUpperNode(n, thisUpperNode);
							e = new Edge(n, tempCps, model.getMaxCapacity());
							controller.addEdgeOnCanvas(e);
						}
						
					}
                }
            }
        }
        if (!found && !outsideCon) {
            int count = 0;
            for (Edge ed : upperNode.getConnections()) {
                AbstractCanvasObject cps;
                if (ed.getA().equals(upperNode)) {
                    cps = ed.getB();
                } else {
                    cps = ed.getA();
                }

                cx = upperNode.getLeftBorder() >> 1;
                cy = (int) (scalediv20 + 5 + (model.getScale() + scalediv20 + 10) * count);

                if (x - controller.getScale() <= cx && y - controller.getScale() <= cy && x >= cx && y >= cy
                        && cps != tempCps) {
                    outsideCon = true;
                    node = false;
                    onEdge = false;
                    for (Edge p : tempCps.getConnections()) {
                        if ((p.getA() == tempCps && p.getB() == cps) || (p.getB() == tempCps && p.getA() == cps)) {
                            newEdge = false;
                            e = p;
                        }
                    }
                    if (!newEdge) {

                        // Node ohne Edge?
                        if (e.getA().getClass() == Node.class && e.getA().getConnections().isEmpty()) {
                            tempCps = e.getA();
                            deleteNode = true;
                        }
                        if (e.getB().getClass() == Node.class && e.getB().getConnections().isEmpty()) {
                            deleteNode = true;
                        }
                    } else {
                        Edge edge = new Edge(cps, tempCps, model.getMaxCapacity());
                        if (outsideCon) {
                        } else {
                            controller.addEdgeOnCanvas(edge);
                        }
                    }
                }
                count++;
            }
        }

        // Edge auf eine Edge gezogen?
        if (onEdge && !checkForReplacement(x, y)) {
            Edge p = mousePositionOnEdge(x, y);
            if (p != null) {
                Edge e1;
                Edge e2;

                node = false;

                Node n = new Node("Node");

                n.setPosition(x, y);
                controller.addObjUpperNode(n, upperNode);

                AbstractCanvasObject r, k;
                r = p.getA();
                k = p.getB();

                e = new Edge(n, tempCps, model.getMaxCapacity());

                e1 = new Edge(n, r, model.getMaxCapacity());

                e2 = new Edge(n, k, model.getMaxCapacity());

                controller.addEdgeOnCanvas(e1);
                controller.addEdgeOnCanvas(e2);
            }
        }else{
        	mayBeReplaced = null;
        }

        // ins leere Gedragged
        if (node && x > upperNode.getLeftBorder() && !checkForReplacement(x, y)) {
            Node n = new Node("Node");

            n.setPosition(x, y);
            controller.addObjUpperNode(n, upperNode);

            e = new Edge(n, tempCps, model.getMaxCapacity());

        }else{
        	mayBeReplaced = null;
        }

        // Wenn ein Node ohne Connections da ist
        if (deleteNode) {
            controller.delCanvasObject(tempCps, true);
            tempCps = null;
        }
    }

    /**
     * Checks if the mouse is on an Edge.
     *
     * @param x Position of the Mouse
     * @param y Position of the Mouse
     * @return CpsEdge the Mouse is on, null if the mouse is not on an Edge
     */
    private Edge mousePositionOnEdge(int x, int y) {
        x += controller.getScaleDiv2();
        y += controller.getScaleDiv2();
        int lx, ly, hx, hy;

//TODO: is this right
//        for (CpsEdge p : upperNode.getNodeEdges()) {
//            Line2D l = new Line2D.Float(p.getA().getPosition().x, p.getA().getPosition().y, p.getB().getPosition().x,
//                    p.getB().getPosition().y);
//
//            int[] positions = determineMousePositionOnEdge(p);
//            lx = positions[0];
//            ly = positions[1];
//            hx = positions[2];
//            hy = positions[3];
//
//            // distance from a point to a line and between both Objects
//            if (l.ptLineDistSq(x - model.getScaleDiv2(), y - model.getScaleDiv2()) < 20 && x > lx && x < hx && y > ly
//                    && y < hy) {
//                return p;
//            }
//        }

        // edge to outside connection

        int count = 0;
        for (Edge e : upperNode.getConnections()) {
            AbstractCanvasObject cps;
            if (e.getA().equals(this.upperNode)) {
                cps = e.getB();
            } else {
                cps = e.getA();
            }
            for (Edge p : cps.getConnections()) {
                AbstractCanvasObject obj = null;
                boolean doTest = false;
                if (upperNode.getNodes().contains(p.getA())) {
                    obj = p.getA();
                    doTest = true;
                } else if (upperNode.getNodes().contains(p.getB())) {
                    obj = p.getB();
                    doTest = true;
                }
                // g2.drawImage(img, (borderPos >> 1) - 25, (int) (scalediv20 +
                // 5 + (50 + scalediv20 + 10) * count), 50, 50, null);
                if (doTest) {
                    Line2D l = new Line2D.Float((upperNode.getLeftBorder() >> 1) - 25,
                            (int) (scalediv20 + 5 + 25 + (50 + scalediv20 + 10) * count), obj.getPosition().x,
                            obj.getPosition().y);
                    if ((upperNode.getLeftBorder() >> 1) > obj.getPosition().x) {
                        hx = (upperNode.getLeftBorder() >> 1) - 25 + model.getScaleDiv2() + 7;
                        lx = obj.getPosition().x + model.getScaleDiv2() - 7;
                    } else {
                        lx = (upperNode.getLeftBorder() >> 1) - 25 + model.getScaleDiv2() - 7;
                        hx = obj.getPosition().x + model.getScaleDiv2() + 7;
                    }
                    if ((int) (scalediv20 + 5 + (50 + scalediv20 + 10) * count) > obj.getPosition().y) {
                        hy = (int) (scalediv20 + 5 + (50 + scalediv20 + 10) * count) + model.getScaleDiv2() + 7;
                        ly = obj.getPosition().y + model.getScaleDiv2() - 7;
                    } else {
                        ly = (int) (scalediv20 + 5 + (50 + scalediv20 + 10) * count) + model.getScaleDiv2() - 7;
                        hy = obj.getPosition().y + model.getScaleDiv2() + 7;
                    }

                    // distance from a point to a line and between both Objects
                    if (l.ptLineDistSq(x - model.getScaleDiv2(), y - model.getScaleDiv2()) < 20 && x > lx && x < hx
                            && y > ly && y < hy) {
                        return p;
                    }
                }
            }
            count++;
        }
        return null;
    }



    /**
     * copies a set of given informations
     *
     * @param informations
     */
    public void setShowedInformation(boolean[] informations) {
        showedInformation = informations;
    }


    /**
     * set tooltIp
     *
     * @param bool
     */
    public void setToolTip(boolean bool) {
        this.toolTip = bool;
    }

    /**
     * Set the Mouse Position on the UpperNodeCanvas;
     *
     * @param x
     * @param y
     */
    public void setXY(int x, int y) {
        this.x = x;
        this.y = y;
    }
    
    @Override
	public boolean checkForReplacement(int x, int y) {
		return checkForReplacement(upperNode.getNodes(), null, x, y);
	}
    

	@Override
	public void tryToAlignObjects(){
		/**
		 * Align all Objects
		 */
		for(AbstractCanvasObject cps: upperNode.getNodes())
			align(cps,3*model.getScaleDiv2());
		
		/**
		 * AutoSave new Positons
		 */
		try{	
			controller.autoSave();
		} catch (IOException ex) {
			System.err.println("AutoSave error by aligning");
			ex.printStackTrace();
		}
	}

	@Override
	public void closeUpperNodeTab(int upperNodeId) {
        JTabbedPane tabbedPane = (JTabbedPane) getParent().getParent().getParent();
        for (int i = 4; i < tabbedPane.getTabCount(); i++) {
            if (((GroupNodeCanvas) ((JScrollPane) tabbedPane.getComponentAt(i)).getViewport()
                    .getComponent(0)).upperNode.getId() == upperNodeId) {
                tabbedPane.remove(i);
                break;
            }
        }
	}

}