/*
 * @(#)DefaultOverlayable.java 7/31/2007
 *
 * Copyright 2002 - 2007 JIDE Software Inc. All rights reserved.
 */
package com.mucommander.ui.main.table;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

/**
 * <code>DefaultOverlayable</code> is the default implementation of <code>Overlayable</code> using JPanel as the base
 * component.
 *
 * This file that is based on the one provided by JIDE Software as part of JIDE Common Layer
 * has been modified to include all its dependencies so it can be used as a standalone resource.
 * This change was done by Arik Hadas, 12/12/2016.
 */
public class DefaultOverlayable extends JPanel implements SwingConstants, ComponentListener {
    public static final String CLIENT_PROPERTY_OVERLAYABLE = "Overlayable.overlayable";

    private JComponent _actualComponent;
    private Insets _overlayLocationInsets = new Insets(0, 0, 0, 0);
    private List<JComponent> _overlayComponents;
    private Map<JComponent, Integer> _overlayLocations;
//    private Map<JComponent, Component> _overlayRelativeComponent;

    public DefaultOverlayable() {
        initComponents();
    }

    public DefaultOverlayable(JComponent component) {
        initComponents();
        setActualComponent(component);
    }

    public DefaultOverlayable(JComponent actualComponent, JComponent overlayComponent, int overlayLocation) {
        initComponents();
        setActualComponent(actualComponent);
        addOverlayComponent(overlayComponent, overlayLocation);
    }

    public DefaultOverlayable(JComponent actualComponent, JComponent overlayComponent) {
        initComponents();
        setActualComponent(actualComponent);
        addOverlayComponent(overlayComponent, SwingConstants.CENTER);
    }

    private void initComponents() {
        setLayout(null);
        _overlayComponents = new Vector();
        _overlayLocations = new Hashtable();
//        _overlayRelativeComponent = new Hashtable();
    }

    /**
     * Override to consider the overlayLocationInsets. If the overlayLocationInsets's edges are positive number, we will
     * increase the preferred size so that the overlayout component can be shown. If they are negative, we will still
     * keep the super.getPreferredSize.
     *
     * @return the preferred size of the DefaultOverlayable.
     */
    @Override
    public Dimension getPreferredSize() {
        Dimension size = getActualComponent() == null ? new Dimension(0, 0) : getActualComponent().getPreferredSize();
        Insets insets = getOverlayLocationInsets();
        if (insets != null) {
            size.width += Math.max(0, insets.left) + Math.max(0, insets.right);
            size.height += Math.max(0, insets.top) + Math.max(0, insets.bottom);
        }
        return size;
    }

    @Override
    public void setBounds(int x, int y, int width, int height) {
        super.setBounds(x, y, width, height);

        Insets insets = getOverlayLocationInsets();
        x = Math.max(0, insets.left);
        y = Math.max(0, insets.top);
        width -= Math.max(0, insets.left) + Math.max(0, insets.right);
        height -= Math.max(0, insets.top) + Math.max(0, insets.bottom);
        getActualComponent().setBounds(x, y, width, height);

        updateLocation();
    }

    private void updateLocation() {
        JComponent[] components = getOverlayComponents();
        for (JComponent c : components) {
            if (c == null) {
                return;
            }

            if (c.isVisible()) {
                Rectangle r = getOverlayComponentBounds(c);
                c.setBounds(r);
            }
        }
    }

    private Rectangle getOverlayComponentBounds(JComponent component) {
//        Component relativeComponent = getOverlayRelativeComponent(component);
//        if (relativeComponent == null) {
//            relativeComponent = getActualComponent();
//        }
        Component relativeComponent = getActualComponent();

        Rectangle bounds = relativeComponent.getBounds();
        if (relativeComponent != getActualComponent()) {
            bounds = SwingUtilities.convertRectangle(relativeComponent.getParent(), bounds, getActualComponent());
        }
        Rectangle overlayBounds = new Rectangle(bounds);
        Insets insets = getOverlayLocationInsets();
        overlayBounds.x -= insets.left;
        overlayBounds.y -= insets.top;
        overlayBounds.width += insets.left + insets.right;
        overlayBounds.height += insets.top + insets.bottom;

        int cx = 0;
        int cy = 0;

        Dimension size = component.getPreferredSize();
        int cw = size.width;
        int ch = size.height;

        switch (getOverlayLocation(component)) {
            case CENTER:
                cx = bounds.x + (bounds.width - cw) / 2;
                cy = bounds.y + (bounds.height - ch) / 2;
                break;
            case NORTH:
                cx = bounds.x + (bounds.width - cw) / 2;
                cy = overlayBounds.y;
                break;
            case SOUTH:
                cx = bounds.x + (bounds.width - cw) / 2;
                cy = overlayBounds.y + overlayBounds.height - ch;
                break;
            case WEST:
                cx = overlayBounds.x;
                cy = bounds.y + (bounds.height - ch) / 2;
                break;
            case EAST:
                cx = overlayBounds.x + overlayBounds.width - cw;
                cy = bounds.y + (bounds.height - ch) / 2;
                break;
            case NORTH_WEST:
                cx = overlayBounds.x;
                cy = overlayBounds.y;
                break;
            case NORTH_EAST:
                cx = overlayBounds.x + overlayBounds.width - cw;
                cy = overlayBounds.y;
                break;
            case SOUTH_WEST:
                cx = overlayBounds.x;
                cy = overlayBounds.y + overlayBounds.height - ch;
                break;
            case SOUTH_EAST:
                cx = overlayBounds.x + overlayBounds.width - cw;
                cy = overlayBounds.y + overlayBounds.height - ch;
                break;
        }

        return new Rectangle(cx, cy, cw, ch);
    }

    public int getOverlayLocation(JComponent component) {
        Integer location = _overlayLocations.get(component);
        if (location != null) {
            return location;
        }
        else {
            return -1;
        }
    }

//    public Component getOverlayRelativeComponent(JComponent component) {
//        Component c = _overlayRelativeComponent.get(component);
//        if (c != null) {
//            return c;
//        }
//        else {
//            return null;
//        }
//    }

    public void setOverlayLocation(JComponent component, int location) {
        setOverlayLocation(component, null, location);
    }

    private void setOverlayLocation(JComponent component, Component relativeComponent, int location) {
        boolean updated = false;
        int old = getOverlayLocation(component);
        if (old != location) {
            _overlayLocations.put(component, location);
            updated = true;
        }
//        Component oldComponent = getOverlayRelativeComponent(component);
//        if (oldComponent != relativeComponent) {
//            _overlayRelativeComponent.put(component, relativeComponent);
//            if(!JideSwingUtilities.isListenerRegistered(component, ComponentListener.class, this)) {
//                component.addComponentListener(this);
//            }
//            updated = true;
//        }
        if (updated) {
            updateLocation();
        }
    }

    public void addOverlayComponent(JComponent component) {
        addOverlayComponent(component, SwingConstants.CENTER, -1);
    }

    public void addOverlayComponent(JComponent component, int location) {
        addOverlayComponent(component, location, -1);
    }

    public void addOverlayComponent(JComponent component, int location, int index) {
        addOverlayComponent(component, null, location, index);
    }

    private void addOverlayComponent(JComponent component, Component relativeComponent, int location, int index) {
        if (_overlayComponents.contains(component)) {
            _overlayComponents.remove(component);
        }
        if (index == -1) {
            _overlayComponents.add(component);
            add(component, getComponentCount() - 1); // add it before the the actual component
        }
        else {
            _overlayComponents.add(index, component);
            add(component, index);
        }
        setOverlayLocation(component, relativeComponent, location);
    }

    public void removeOverlayComponent(JComponent component) {
        if (_overlayComponents.contains(component)) {
            _overlayComponents.remove(component);
            _overlayLocations.remove(component);
//            _overlayRelativeComponent.remove(component);
            remove(component);
        }
    }

    public JComponent[] getOverlayComponents() {
        return _overlayComponents.toArray(new JComponent[_overlayComponents.size()]);
    }

    public JComponent getActualComponent() {
        return _actualComponent;
    }

    public void setActualComponent(JComponent actualComponent) {
        if (_actualComponent != null) {
            remove(_actualComponent);
            _actualComponent.putClientProperty(CLIENT_PROPERTY_OVERLAYABLE, null);
        }
        _actualComponent = actualComponent;
        _actualComponent.putClientProperty(CLIENT_PROPERTY_OVERLAYABLE, this);
        add(_actualComponent);
        Container container = getParent();
        if (container != null) {
            invalidate();
            container.validate();
        }
    }


    public Insets getOverlayLocationInsets() {
        return _overlayLocationInsets;
    }

    public void setOverlayLocationInsets(Insets overlayLocationInsets) {
        _overlayLocationInsets = overlayLocationInsets;
        Container container = getParent();
        if (container != null) {
            invalidate();
            container.validate();
        }
    }


    public void setOverlayVisible(boolean visible) {
        JComponent[] components = getOverlayComponents();
        for (JComponent component : components) {
            component.setVisible(visible);
        }
    }

    public void componentResized(ComponentEvent e) {
        updateLocation();
    }

    public void componentMoved(ComponentEvent e) {
        updateLocation();
    }

    public void componentShown(ComponentEvent e) {
        updateLocation();
    }

    public void componentHidden(ComponentEvent e) {
        updateLocation();
    }
}