package ui.view;

import classes.*;
import com.google.gson.JsonParseException;
import ui.controller.Control;
import ui.controller.UpdateController;
import ui.model.Model;

import javax.swing.*;
import java.awt.*;
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.io.File;
import java.io.IOException;
import java.util.ArrayList;

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

    private static final long serialVersionUID = 1L;
    // The UpperNode
    public CpsUpperNode upperNode;
    // Path
    protected String path;
    int code;
    private JLabel breadCrumb;


    /**
     * Constructor.
     *
     * @param mod       the Model
     * @param control   the Controller
     * @param unitGraph
     */
    UpperNodeCanvas(Model mod, Control control, UnitGraph unitGraph, CpsUpperNode UpperNode, String parentPath) {
        toolTip = false;

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

        // Cps objecte aus dem border links schieben
        upperNode.setLeftBorder((int) (50 + scalediv20 + scalediv20 + 10));
        for (AbstractCpsObject 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.addSeparator();
        popmenu.add(itemGroup);
        popmenu.add(itemUngroup);
        popmenu.add(itemTrack);
        popmenu.add(itemUntrack);

        itemDelete.setEnabled(false);
        itemCut.setEnabled(false);
        itemCopy.setEnabled(false);
        itemPaste.setEnabled(true);
        itemGroup.setEnabled(false);
        itemUngroup.setEnabled(false);
        itemTrack.setEnabled(false);
        itemUntrack.setEnabled(false);
        updCon = new UpdateController(model, controller);

        itemGroup.addActionListener(actionEvent -> {
            // calculate uppernode pos (taken from the controller)
            unPos = new Position(0, 0);
            animCps = new ArrayList<>();
            for (AbstractCpsObject 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 (AbstractCpsObject 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("NodeOfNode", upperNode, model.getSelectedCpsObjects());
                    controller.calculateStateForCurrentTimeStep();
                    repaint();
                }
            });
            animT.start();
        });

        itemUngroup.addActionListener(actionEvent -> {
            // save old Position
            JTabbedPane tabbedPane = (JTabbedPane) getParent().getParent().getParent();
            for (int i = 4; i < tabbedPane.getTabCount(); i++) {
                if (((UpperNodeCanvas) ((JScrollPane) tabbedPane.getComponentAt(i)).getViewport()
                        .getComponent(0)).upperNode.getId() == tempCps.getId()) {
                    tabbedPane.remove(i);
                    break;
                }
            }

            savePos = new ArrayList<>();
            animCps = ((CpsUpperNode) tempCps).getNodes();
            controller.delUpperNode((CpsUpperNode) 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 (AbstractCpsObject 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.calculateStateForCurrentTimeStep();
                    repaint();
                }
            });
            animT.start();
        });

        itemTrack.addActionListener(actionEvent -> {
            for (AbstractCpsObject o : model.getSelectedCpsObjects()) {
                if (o instanceof HolonObject) {
                    boolean found = false;
                    if (controller.getTrackingObj() != null) {
                        for (AbstractCpsObject obj : controller.getTrackingObj()) {
                            if (obj instanceof HolonObject) {
                                if (obj.getId() == o.getId()) {
                                    found = true;
                                }
                            }
                        }
                    }
                    if (!found) {
                        controller.addTrackingObj(o);
                        ((HolonObject) o).updateTrackingInfo();
                    }
                    if (model.getShowConsoleLog()) {
                        controller.addTextToConsole("Tracking: ", Color.BLACK, 12, false, false, false);
                        controller.addTextToConsole("" + o.getName(), Color.BLUE, 12, true, false, false);
                        controller.addTextToConsole(", ID:", Color.BLACK, 12, false, false, false);
                        controller.addTextToConsole("" + o.getId(), Color.RED, 12, true, false, true);
                    }
                }
            }
        });

        itemUntrack.addActionListener(actionEvent -> {
            for (AbstractCpsObject o : model.getSelectedCpsObjects()) {
                if (o instanceof HolonObject) {
                    boolean found = false;
                    if (controller.getTrackingObj() != null) {
                        for (AbstractCpsObject obj : controller.getTrackingObj()) {
                            if (obj instanceof HolonObject) {
                                if (obj.getId() == o.getId()) {
                                    found = true;
                                }
                            }
                        }
                    }
                    if (found) {
                        // Removed from tracking array and tracking
                        // information reseted
                        controller.removeTrackingObj(o);
                        ((HolonObject) o).setTrackingProd(new float[100]);
                        ((HolonObject) o).setTrackingCons(new float[100]);
                    }
                }
            }
            System.out.println(controller.getTrackingObj());
        });

        itemDelete.addActionListener(actionEvent -> {
            // Remove the selected Object objects
            for (AbstractCpsObject cps : model.getSelectedCpsObjects()) {
                if (upperNode.getNodes().contains(cps)) {
                    controller.delObjUpperNode(cps, upperNode);
                    // Removes the object from the tracked objects, in case
                    // it was tracked
                    controller.removeTrackingObj(cps);
                    // Remove UpperNodeTab if UpperNode deleted
                    if (cps instanceof CpsUpperNode) {
                        JSplitPane tempSplit = (JSplitPane) getParent().getParent().getParent().getParent();
                        JTabbedPane tabbedPane;
                        JTabbedPane tabbedPane2;
                        // if SplitView is activated
                        if (tempSplit.getLeftComponent() instanceof JTabbedPane
                                && tempSplit.getRightComponent() instanceof JTabbedPane) {
                            tabbedPane = (JTabbedPane) tempSplit.getLeftComponent();
                            tabbedPane2 = (JTabbedPane) tempSplit.getRightComponent();
                        } else {
                            tabbedPane = (JTabbedPane) tempSplit.getLeftComponent();
                            tabbedPane2 = null;
                        }
                        // Look if the uppernode is open in a Tab
                        for (int i = 4; i < tabbedPane.getTabCount(); i++) {

                            if (tabbedPane.getComponentAt(i) != null &&
                                    ((UpperNodeCanvas) ((JScrollPane) tabbedPane.getComponentAt(i)).getViewport()
                                            .getComponent(0)).upperNode.getId() == cps.getId()) {
                                ((ButtonTabComponent) tabbedPane.getTabComponentAt(i)).removeTabs();
                                break;
                            }
                        }
                        // If SplitView is on and the view on
                        // tabbedPane2 is the deleted upperNode
                        try {
                            if (tabbedPane2 != null
                                    && ((UpperNodeCanvas) ((JScrollPane) tabbedPane2.getSelectedComponent())
                                    .getViewport().getComponent(0)).upperNode.getId() == cps.getId()) {
                                ((ButtonTabComponent) tabbedPane.getTabComponentAt(tabbedPane2.getSelectedIndex()))
                                        .removeTabs();
                            }
                        } catch (Exception e2) {
                        }

                    }
                }
            }
            toolTip = false;
            model.getSelectedCpsObjects().clear();
            tempCps = null;
            repaint();
        });

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

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

        itemPaste.addActionListener(actionEvent -> {
            try {
                controller.paste(upperNode, mousePosition);
                unitGraph.update(model.getSelectedCpsObjects());
            } 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();
        });

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

    /**
     * Paints all Components on the Canvas.
     *
     * @param g Graphics
     */
    public void paintComponent(Graphics g) {
        String maxCap = null;
        super.paintComponent(g);
        ((JScrollPane) this.getParent().getParent()).setColumnHeaderView(breadCrumb);
        // Rendering
        g2 = (Graphics2D) g;
        RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHints(rh);

        // Paint the Background
        if (!upperNode.getImagePath().isEmpty()) {
            img = new ImageIcon(upperNode.getImagePath()).getImage();
            switch (upperNode.getBackgroundMode()) {
                case BackgroundPopUp.IMAGE_PIXELS:
                    g2.drawImage(img, upperNode.getLeftBorder(), 0, img.getWidth(null), img.getHeight(null), null);
                    break;
                case BackgroundPopUp.STRETCHED:
                    g2.drawImage(img, upperNode.getLeftBorder(), 0, model.getCanvasX(), model.getCanvasY(), null);
                    break;
                case BackgroundPopUp.CUSTOM:
                    g2.drawImage(img, upperNode.getLeftBorder(), 0, upperNode.getImageWidht(), upperNode.getImageHeight(),
                            null);
                    break;
                default:
                    break;
            }
        }

        // Draw Left Border
        g2.setColor(new Color(230, 230, 230));
        g2.fillRect(0, 0, upperNode.getLeftBorder(), this.getHeight());
        g2.setColor(Color.BLACK);
        g2.drawLine(0, 0, this.getWidth(), 0);

        // Test SubNet Coloring
        int i = 0;
        for (SubNet s : controller.getSimManager().getSubNets()) {

            if (model.getSubNetColors().size() - 1 < i) {
                controller.addSubNetColor(new Color((int) (Math.random() * 255), (int) (Math.random() * 255),
                        (int) (Math.random() * 255)));
            }

            for (HolonObject cps : s.getObjects()) {
                cps.setBorderColor(model.getSubNetColors().get(i));
            }
            i++;
        }

        // drawEdges that is being dragged
        if (drawEdge) {
            g2.setColor(Color.BLACK);
            g2.setStroke(new BasicStroke(2));

            // If TempCps is an outside Object
            if (!upperNode.getNodes().contains(tempCps)) {
                int count = 0;
                for (CpsEdge e : upperNode.getConnections()) {
                    if (e.getA().equals(tempCps)) {
                        g2.drawLine(upperNode.getLeftBorder() >> 1, (int) (model.getScaleDiv2() + scalediv20 + 5
                                + (model.getScale() + scalediv20 + 10) * count), x, y);
                    } else if (e.getB().equals(tempCps)) {
                        g2.drawLine(upperNode.getLeftBorder() >> 1, (int) (model.getScaleDiv2() + scalediv20 + 5
                                + (model.getScale() + scalediv20 + 10) * count), x, y);
                    }
                    count++;
                }
            } else {
                g2.drawLine(tempCps.getPosition().x, tempCps.getPosition().y, x, y);
            }
        }
        // draw Edges
        for (CpsEdge con : upperNode.getNodeEdges()) {
            maxCap = paintEdge(con, maxCap);
        }

        // Objects connected to upperNode
        int count = 0;
        for (CpsEdge e : upperNode.getConnections()) {
            AbstractCpsObject cps;
            if (e.getA().equals(this.upperNode)) {
                cps = e.getB();
            } else {
                cps = e.getA();
            }
            // Show and Highlight
            if (model.getSelectedCpsObjects().contains(cps) || showedInformation[4]) {
                for (CpsEdge ed : cps.getConnections()) {
                    AbstractCpsObject obj = null;
                    if (upperNode.getNodes().contains(ed.getA())) {
                        obj = ed.getA();
                    } else if (upperNode.getNodes().contains(ed.getB())) {
                        obj = ed.getB();
                    }
                    if (obj != null) {
                        if (ed.getConnected() == 0) {

                            setEdgeState(ed);

                            if (ed.getA().getId() == model.getSelectedObjectID()
                                    || ed.getB().getId() == model.getSelectedObjectID() || edgeHighlight == ed)
                                g2.setColor(Color.BLUE);
                        } else {
                            g2.setColor(Color.DARK_GRAY);
                            g2.setStroke(new BasicStroke(2));
                        }
                        g2.drawLine(obj.getPosition().x, obj.getPosition().y, (upperNode.getLeftBorder() >> 1),
                                (int) (scalediv20 + 5 + (50 + scalediv20 + 10) * count) + 25);
                        if (showedInformation[0]) {

                            maxCap = setCapacityString(ed, maxCap);

                            if (ed.getConnected() == 0 || ed.getConnected() == 1) {
                                g2.drawString(ed.getFlow() + "/" + maxCap,
                                        (obj.getPosition().x + (upperNode.getLeftBorder() >> 1)) / 2,
                                        (obj.getPosition().y + (int) (scalediv20 + 5 + (50 + scalediv20 + 10) * count)
                                                + 25) / 2);
                            } else {
                                g2.drawString("not connected",
                                        (obj.getPosition().x + (upperNode.getLeftBorder() >> 1)) / 2,
                                        (obj.getPosition().y + (int) (scalediv20 + 5 + (50 + scalediv20 + 10) * count)
                                                + 25) / 2);
                            }
                        }
                    }
                }
            }

            // Border Highlighting
            if (showedInformation[3]) {
                g2.setColor(cps.getBorderColor());
                if (g2.getColor() != Color.WHITE) {
                    g2.fillRect((int) ((upperNode.getLeftBorder() >> 1) - 25 - scalediv20) - 3,
                            (int) (scalediv20 + 5 + (25 + scalediv20 + 10) * count - scalediv20) - 3,
                            (int) (50 + ((scalediv20 + 3) * 2)), (int) (50 + ((scalediv20 + 3) * 2)));
                }
            }

            // node image
            if (cps instanceof CpsNode && (cps == tempCps || model.getSelectedCpsObject() == cps
                    || model.getSelectedCpsObjects().contains(cps) || tempSelected.contains(cps))) {
                //img = new ImageIcon(this.getClass().getResource("/Images/node_selected.png")).getImage();//TODO: ONE MORE
            	img = Util.loadImage(this,"/Images/node_selected.png");
            } else {
                if (cps instanceof HolonSwitch) {
                    if (((HolonSwitch) cps).getActiveAt()[model.getCurIteration()]) {
                        ((HolonSwitch) cps).setAutoState(true);
                    } else {
                        ((HolonSwitch) cps).setAutoState(false);
                    }
                }
                // Highlighting
                if ((cps == tempCps && model.getSelectedCpsObjects().size() == 0 && tempSelected.size() == 0)
                        || model.getSelectedCpsObjects().contains(cps) || tempSelected.contains(cps)) {
                    g2.setColor(Color.BLUE);
                    g2.fillRect((int) ((upperNode.getLeftBorder() >> 1) - 25 - scalediv20),
                            (int) (scalediv20 + 5 + (50 + scalediv20 + 10) * count - scalediv20),
                            (int) (50 + (scalediv20 * 2)), (int) (50 + (scalediv20 * 2)));
                } else if (cps instanceof HolonObject) {
                    g2.setColor(((HolonObject) cps).getColor());

                    g2.fillRect((int) ((upperNode.getLeftBorder() >> 1) - 25 - scalediv20),
                            (int) (scalediv20 + 5 + (50 + scalediv20 + 10) * count - scalediv20),
                            (int) (50 + (scalediv20 * 2)), (int) (50 + (scalediv20 * 2)));
                }
                // draw image
                File checkPath = new File(cps.getImage());
                if (checkPath.exists()) {
                    img = new ImageIcon(cps.getImage()).getImage();
                } else {
                    //img = new ImageIcon(this.getClass().getResource(cps.getImage())).getImage();//TODO: and I'm done
                	img = Util.loadImage(this,cps.getImage());
                }
            }
            g2.drawImage(img, (upperNode.getLeftBorder() >> 1) - 25,
                    (int) (scalediv20 + 5 + (50 + scalediv20 + 10) * count), 50, 50, null);
            count++;
        }
        // Highlighted Edge
        if (model.getSelectedObjectID() > 0 || !model.getSelectedCpsObjects().isEmpty() || !tempSelected.isEmpty()) {
            g2.setColor(Color.BLUE);
            for (CpsEdge con : upperNode.getNodeEdges()) {
                if (con.getFlow() <= con.getCapacity()) {
                    g2.setStroke(new BasicStroke(Math.min((con.getFlow() / con.getCapacity() * 3) + 1, 4)));
                } else {
                    g2.setStroke(new BasicStroke(2));
                }

                maxCap = drawEdgeLine(con, maxCap);
            }
        } else if (edgeHighlight != null) {
            g2.setColor(Color.BLUE);
            if (edgeHighlight.getFlow() <= edgeHighlight.getCapacity()) {
                g2.setStroke(
                        new BasicStroke(Math.min((edgeHighlight.getFlow() / edgeHighlight.getCapacity() * 3) + 1, 4)));
            } else {
                g2.setStroke(new BasicStroke(2));
            }
            if (upperNode.getNodeEdges().contains(edgeHighlight)) {
                g2.drawLine(edgeHighlight.getA().getPosition().x, edgeHighlight.getA().getPosition().y,
                        edgeHighlight.getB().getPosition().x, edgeHighlight.getB().getPosition().y);

                maxCap = setCapacityString(edgeHighlight, maxCap);

                if (showedInformation[0]) {
                    g2.drawString(edgeHighlight.getFlow() + "/" + maxCap,
                            (edgeHighlight.getA().getPosition().x + edgeHighlight.getB().getPosition().x) / 2,
                            (edgeHighlight.getA().getPosition().y + edgeHighlight.getB().getPosition().y) / 2);
                }
            }
        }

        // Objects in upper node
        for (AbstractCpsObject cps : upperNode.getNodes()) {
            // Border Highlighting
            if (showedInformation[3]) {
                g2.setColor(cps.getBorderColor());
                if (g2.getColor() != Color.WHITE) {
                    g2.fillRect((int) (cps.getPosition().x - controller.getScaleDiv2() - scalediv20 - 3),
                            (int) (cps.getPosition().y - model.getScaleDiv2() - scalediv20 - 3),
                            (int) (controller.getScale() + ((scalediv20 + 3) * 2)),
                            (int) (controller.getScale() + ((scalediv20 + 3) * 2)));
                }
            }

            setEdgePictureAndHighlighting(cps);

            g2.drawImage(img, cps.getPosition().x - model.getScaleDiv2(), cps.getPosition().y - model.getScaleDiv2(),
                    controller.getScale(), controller.getScale(), null);
        }

        // Dragged marker Highlighting
        g2.setStroke(new BasicStroke(1));
        if (doMark) {
            g2.setColor(Color.BLACK);
            drawMarker();
        }

        // Border Line
        g2.setColor(Color.BLACK);
        g2.drawLine(upperNode.getLeftBorder(), 0, upperNode.getLeftBorder(), this.getHeight());

        // Tooltip
        showTooltip(g);
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        triggerUpdateController();
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
        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 (AbstractCpsObject 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;

                    setConsoleTextAfterSelect(cps);

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

                    // 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 (CpsEdge ed : upperNode.getConnections()) {
                AbstractCpsObject 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 (model.getShowConsoleLog()) {
                        controller.addTextToConsole("Selected: ", Color.BLACK, 12, false, false, false);
                        controller.addTextToConsole("" + cps.getName(), Color.BLUE, 12, true, false, false);
                        controller.addTextToConsole(", ID:", Color.BLACK, 12, false, false, false);
                        controller.addTextToConsole("" + cps.getId(), Color.RED, 12, true, false, true);
                        controller.setSelectedObjectID(tempCps.getId());
                    }
                    // 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) {
        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 {
                controller.autoSave();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

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

        dragged = false;

        // Rightclick List
        setRightClickMenu(e);

        markObjects();

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

        controller.calculateStateForTimeStep(model.getCurIteration());
        triggerUpdateController();
        repaint();

    }

    @Override
    public void mouseDragged(MouseEvent e) {
        // 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 (AbstractCpsObject 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);
                        }
                    }
                }
                repaint();
            } catch (Exception eex) {

            }
        }

        // Mark Objects
        if (doMark) {
            tempSelected.clear();
            for (AbstractCpsObject 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 (CpsEdge ed : upperNode.getConnections()) {
                AbstractCpsObject 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 (AbstractCpsObject cps : upperNode.getNodes()) {

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

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

            AbstractCpsObject 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
        CpsEdge e = null;
        for (AbstractCpsObject 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 (CpsEdge p : tempCps.getConnections()) {
                    if ((p.getA() == tempCps && p.getB() == cps) || (p.getB() == tempCps && p.getA() == cps)) {
                        newEdge = false;
                        e = p;
                    }
                }
                if (!newEdge) {
                    if (outsideCon) {
                        controller.disconnectNodes(e, upperNode);
                    } else {
                        controller.delEdgeUpperNode(e, upperNode);
                    }
                    // Node ohne Edge?
                    if (e.getA().getClass() == CpsNode.class && e.getA().getConnections().isEmpty()) {
                        tempCps = e.getA();
                        deleteNode = true;
                    }
                    if (e.getB().getClass() == CpsNode.class && e.getB().getConnections().isEmpty()) {
                        deleteNode = true;
                    }
                } else {
                    e = new CpsEdge(cps, tempCps, model.getMaxCapacity());
                    if (outsideCon) {
                        controller.connectNodes(e, upperNode);
                    } else {
                        controller.addEdgeUpperNode(e, upperNode);
                    }
                }
            }
        }
        if (!found && !outsideCon) {
            int count = 0;
            for (CpsEdge ed : upperNode.getConnections()) {
                AbstractCpsObject 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 (CpsEdge p : tempCps.getConnections()) {
                        if ((p.getA() == tempCps && p.getB() == cps) || (p.getB() == tempCps && p.getA() == cps)) {
                            newEdge = false;
                            e = p;
                        }
                    }
                    if (!newEdge) {
                        if (outsideCon) {
                            controller.disconnectNodes(e, upperNode);
                        } else {
                            controller.delEdgeUpperNode(e, upperNode);
                        }
                        // Node ohne Edge?
                        if (e.getA().getClass() == CpsNode.class && e.getA().getConnections().isEmpty()) {
                            tempCps = e.getA();
                            deleteNode = true;
                        }
                        if (e.getB().getClass() == CpsNode.class && e.getB().getConnections().isEmpty()) {
                            deleteNode = true;
                        }
                    } else {
                        CpsEdge edge = new CpsEdge(cps, tempCps, model.getMaxCapacity());
                        if (outsideCon) {
                            controller.connectNodes(edge, upperNode);
                        } else {
                            controller.addEdgeUpperNode(edge, upperNode);
                        }
                    }
                }
                count++;
            }
        }

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

                node = false;

                CpsNode n = new CpsNode("Node");

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

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

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

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

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

                controller.delEdgeUpperNode(p, upperNode);
                if (outsideCon) {
                    controller.connectNodes(e, upperNode);
                } else {
                    controller.addEdgeUpperNode(e, upperNode);
                }

                controller.addEdgeUpperNode(e1, upperNode);
                controller.addEdgeUpperNode(e2, upperNode);
            }
        }

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

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

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

            if (outsideCon) {
                controller.connectNodes(e, upperNode);
            } else {
                controller.addEdgeUpperNode(e, upperNode);
            }
        }

        // 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 CpsEdge mousePositionOnEdge(int x, int y) {
        x += controller.getScaleDiv2();
        y += controller.getScaleDiv2();
        int lx, ly, hx, hy;


        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 (CpsEdge e : upperNode.getConnections()) {
            AbstractCpsObject cps;
            if (e.getA().equals(this.upperNode)) {
                cps = e.getB();
            } else {
                cps = e.getA();
            }
            for (CpsEdge p : cps.getConnections()) {
                AbstractCpsObject 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;
    }

//    /**
//     * sets the Edge Capacity.
//     *
//     * @param cap capacity
//     */
//    public void setEdgeCapacity(float cap) {
//        controller.setMaxCapacity(cap);
//    }

//    /**
//     * Returns if Information should be shown.
//     *
//     * @return Array of boolean [0] = connection, [1] = objects
//     */
//    public boolean[] getShowedInformation() {
//        return showedInformation;
//    }

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

//    /**
//     * Set the Background Image;
//     *
//     * @param imagePath Image Path
//     * @param mode      Image Mode
//     * @param width     Image custom width
//     * @param height    Image custom height
//     */
//    public void setBackgroundImage(String imagePath, int mode, int width, int height) {
//        upperNode.setBackgroundImage(imagePath, mode, width, height);
//    }

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

    /**
     * Set the Mouse Position on the UpperNodeCanvas;
     *
     * @param x
     * @param y
     */
    void setXY(int x, int y) {
        this.x = x;
        this.y = y;
    }

}