package com.kit.gui.util;


import com.kit.gui.view.MainView;

import java.awt.*;
import java.awt.event.*;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

/**
 * This class allows you to move a Component by using a mouse. The Component
 * moved can be a high level Window (ie. Window, Frame, Dialog) in which case
 * the Window is moved within the desktop. Or the Component can belong to a
 * Container in which case the Component is moved within the Container.
 * <p/>
 * When moving a Window, the listener can be added to a child Component of
 * the Window. In this case attempting to move the child will result in the
 * Window moving. For example, you might create a custom "Title Bar" for an
 * undecorated Window and moving of the Window is accomplished by moving the
 * title bar only. Multiple components can be registered as "window movers".
 * <p/>
 * Components can be registered when the class is created. Additional
 * components can be added at any time using the registerComponent() method.
 */
public class ComponentMover extends MouseAdapter {
    private Insets dragInsets = new Insets(0, 0, 0, 0);
    private Dimension snapSize = new Dimension(1, 1);
    private Rectangle bounds = new Rectangle(0, 0, 0, 0);
    private boolean changeCursor = true;

    private Component destinationComponent;
    private Component destination;
    private Component source;

    private Point pressed;
    private Point location;

    private Cursor originalCursor;
    private boolean autoscrolls;
    private boolean potentialDrag;

    /**
     * Constructor to specify a parent component that will be moved when drag
     * events are generated on a registered child component.
     *
     * @param destinationComponent the component drage events should be forwareded to
     * @param components           the Components to be registered for forwarding drag
     *                             events to the parent component to be moved
     */
    public ComponentMover(Component destinationComponent, Component... components) {
        this.destinationComponent = destinationComponent;
        registerComponent(components);
        getBoundingSize(destinationComponent);
    }

    /**
     * Add the required listeners to the specified component
     *
     * @param components the component the listeners are added to
     */
    public void registerComponent(Component... components) {
        for (Component component : components) {
            component.addMouseListener(this);
        }
    }

    /**
     * Setup the variables used to control the moving of the component:
     * <p/>
     * source - the source component of the mouse event
     * destination - the component that will ultimately be moved
     * pressed - the Point where the mouse was pressed in the destination
     * component coordinates.
     */
    @Override
    public void mousePressed(MouseEvent e) {
        source = e.getComponent();
        int width = source.getSize().width - dragInsets.left - dragInsets.right;
        int height = source.getSize().height - dragInsets.top - dragInsets.bottom;
        Rectangle r = new Rectangle(dragInsets.left, dragInsets.top, width, height);

        if (r.contains(e.getPoint())) {
            setupForDragging(e);
        }
    }

    private void setupForDragging(MouseEvent e) {
        source.addMouseMotionListener(this);
        potentialDrag = true;

        //  Determine the component that will ultimately be moved

        if (destinationComponent != null) {
            destination = destinationComponent;
        }

//        if (destination instanceof MainView) {
//            final MainView gui = (MainView) destination;
//            if (gui.isMaximized()) {
//                return;
//            }
//        }

        pressed = e.getLocationOnScreen();
        location = destination.getLocation();

        if (changeCursor) {
            originalCursor = source.getCursor();
            source.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));

        }

        //  Making sure autoscrolls is false will allow for smoother dragging of
        //  individual components

        if (destination instanceof JComponent) {
            JComponent jc = (JComponent) destination;
            autoscrolls = jc.getAutoscrolls();
            jc.setAutoscrolls(false);
        }
    }

    /**
     * Move the component to its new location. The dragged Point must be in
     * the destination coordinates.
     */
    @Override
    public void mouseDragged(MouseEvent e) {

        Point dragged = e.getLocationOnScreen();
        int dragX = getDragDistance(dragged.x, pressed.x, snapSize.width);
        int dragY = getDragDistance(dragged.y, pressed.y, snapSize.height);

//        if (dragX != 0 || dragY != 0) {
//            if (destination instanceof MainView) {
//                final MainView gui = (MainView) destination;
//                if (gui.isMa) {
//                    return;
//                }
//            }
//        }

        int locationX = location.x + dragX;
        int locationY = location.y + dragY;

        //  Mouse dragged events are not generated for every pixel the mouse
        //  is moved. Adjust the location to make sure we are still on a
        //  snap value.

        getBoundingSize(destination);


        //If the component is a MainView and it is maximized... lets restore

        while (locationX < bounds.x) {
            locationX += snapSize.width;
        }

        while (locationY < bounds.y) {
            locationY += snapSize.height;
        }

        while (locationX > bounds.width) {
            locationX -= snapSize.width;
        }

        while (locationY > bounds.height) {
            locationY -= snapSize.height;
        }


        //  Adjustments are finished, move the component
        destination.setLocation(locationX, locationY);
    }

    /*
     *  Determine how far the mouse has moved from where dragging started
     *  (Assume drag direction is down and right for positive drag distance)
     */
    private int getDragDistance(int larger, int smaller, int snapSize) {
        int halfway = snapSize / 2;
        int drag = larger - smaller;
        drag += (drag < 0) ? -halfway : halfway;
        drag = (drag / snapSize) * snapSize;

        return drag;
    }

    /*
     *  Get the bounds of the parent of the dragged component.
     */
    private void getBoundingSize(Component source) {
        if (source instanceof Window) {
            final Rectangle bounds = new Rectangle(0, 0, 0, 0);
            GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
            for (final GraphicsDevice gd : env.getScreenDevices()) {
                final Rectangle gBounds = gd.getDefaultConfiguration().getBounds();
                if (bounds.x > gBounds.x) {
                    bounds.x = gBounds.x;
                }
                if (bounds.y > gBounds.y) {
                    bounds.y = gBounds.y;
                }
                bounds.width += gBounds.width + 100;
                bounds.height += gBounds.height + 100;
            }
            this.bounds = bounds;
        } else {
            this.bounds = source.getBounds();
        }
    }

    /**
     * Restore the original state of the Component
     */
    @Override
    public void mouseReleased(MouseEvent e) {
        if (!potentialDrag) {
            return;
        }

        source.removeMouseMotionListener(this);
        potentialDrag = false;

        if (changeCursor) {
            source.setCursor(originalCursor);
        }

        if (destination instanceof JComponent) {
            ((JComponent) destination).setAutoscrolls(autoscrolls);
        }
    }
}