/*
 * Created on Mar 11, 2005
 */
package org.flexdock.docking.drag.effects;

import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.util.Map;

import javax.swing.JTabbedPane;

import org.flexdock.docking.Dockable;
import org.flexdock.docking.DockingPort;
import org.flexdock.docking.RegionChecker;
import org.flexdock.docking.defaults.DefaultRegionChecker;
import org.flexdock.util.SwingUtility;

/**
 * @author Christopher Butler
 */
public abstract class DefaultPreview implements DragPreview {
    public static final int DEFAULT_TAB_WIDTH = 50;
    public static final int DEFAULT_TAB_HEIGHT = 20;

    public Polygon createPreviewPolygon(Component dockable, DockingPort port, Dockable hover, String targetRegion, Component paintingTarget, Map dragInfo) {
        if(dockable==null || port==null || targetRegion==null || paintingTarget==null)
            return null;

        if(UNKNOWN_REGION.equals(targetRegion) || !port.isDockingAllowed(dockable, targetRegion))
            return null;

        // if we're not hovering over another Dockable then the DockingPort we're over is empty.
        // return its bounds.
        if(hover==null) {
            Rectangle portBounds = ((Component)port).getBounds();
            return createPolyRect(portBounds);
        }

        Polygon p = null;
        Component srcAxes = hover.getComponent();
        if(isOuterRegion(targetRegion))
            p = createPolyRect(port, srcAxes, targetRegion);
        else {
            p = createPolyTab(port, srcAxes);
            srcAxes = (Component)port;
        }

        SwingUtility.translate(srcAxes, p, paintingTarget);
        return p;
    }

    protected Polygon createPolyRect(DockingPort port, Component dockable, String region) {
        RegionChecker regionChecker = port.getDockingProperties().getRegionChecker();
        if(regionChecker==null)
            regionChecker = new DefaultRegionChecker();

        Rectangle r = regionChecker.getSiblingBounds(dockable, region);
        return createPolyRect(r);
    }

    protected Polygon createPolyRect(Rectangle r) {
        if(r==null)
            return null;

        int x2 = r.x+r.width;
        int y2 = r.y+r.height;
        int[] x = new int[] {r.x, x2, x2, r.x};
        int[] y = new int[] {r.y, r.y, y2, y2};
        return new Polygon(x, y, 4);
    }


    protected Polygon createPolyTab(DockingPort port, Component hover) {
        Component c = port.getDockedComponent();

        Rectangle tabPaneRect = createTabbedPaneRect(port, hover);
        // if no existing component and no singleTabs allowed,
        // return the entire pane bounds
        if(c==null && port.getDockingProperties().isSingleTabsAllowed()==Boolean.FALSE) {
            return createPolyRect(tabPaneRect);
        }

        Rectangle tabRect = new Rectangle(tabPaneRect.x, tabPaneRect.y, DEFAULT_TAB_WIDTH, DEFAULT_TAB_HEIGHT);
        boolean tabsOnTop = port.getDockingProperties().getTabPlacement().intValue()==JTabbedPane.TOP;
        // if 'c' is a JTabbedPane, then there is already a tab out there and
        // we can model our bounds off of it.
        if(c instanceof JTabbedPane) {
            JTabbedPane tabs = (JTabbedPane)c;
            Rectangle lastTab = tabs.getBoundsAt(tabs.getTabCount()-1);
            tabRect.height = lastTab.height;
            tabRect.y = lastTab.y;
            tabRect.x = lastTab.x + lastTab.width;
            tabsOnTop = tabs.getTabPlacement()==JTabbedPane.TOP;
        } else {
            tabRect.y = tabsOnTop? 0: tabPaneRect.height - DEFAULT_TAB_HEIGHT;
            // if there is already a component in the docking port, then our new
            // component will be dropped into the second tab, not the first
            if(c!=null)
                tabRect.x += DEFAULT_TAB_WIDTH;
        }

        // subtract tab height from the pane-rect height, and shift its location
        // down if the tab sits on top
        tabPaneRect.height -= tabRect.height;
        if(tabsOnTop)
            tabPaneRect.y += tabRect.height;

        if(tabsOnTop)
            return createPolyTabOnTop(tabPaneRect, tabRect);
        else
            return createPolyTabOnBottom(tabPaneRect, tabRect);
    }

    protected Rectangle createTabbedPaneRect(DockingPort port, Component hover) {
        // get the bounds and reset location to (0, 0), since we'll be
        // converting coordinates from the DockingPort, not its parent
        Rectangle tabPaneRect = ((Component)port).getBounds();
        tabPaneRect.setLocation(0, 0);
        return tabPaneRect;
    }

    protected Polygon createPolyTabOnTop(Rectangle tabPane, Rectangle tab) {
        Polygon p = new Polygon();
        int tabRight = tab.x + tab.width;
        int paneRight = tabPane.x + tabPane.width;
        int paneBottom = tabPane.y + tabPane.height;

        // if the tab isn't at the origin, then build the path
        // until we reach the tab
        if(tab.x!=0) {
            p.addPoint(tabPane.x, tabPane.y);
            p.addPoint(tab.x, tabPane.y);
        }
        p.addPoint(tab.x, tab.y);
        p.addPoint(tabRight, tab.y);
        p.addPoint(tabRight, tabPane.y);
        p.addPoint(paneRight, tabPane.y);

        // create the right-side
        p.addPoint(paneRight, paneBottom);
        // create the bottom
        p.addPoint(tabPane.x, paneBottom);

        return p;
    }

    protected Polygon createPolyTabOnBottom(Rectangle tabPane, Rectangle tab) {
        Polygon p = new Polygon();
        int tabRight = tab.x + tab.width;
        int paneRight = tabPane.x + tabPane.width;
        int paneBottom = tabPane.y + tabPane.height;
        int tabBottom = paneBottom + tab.height;

        // create the top
        p.addPoint(tabPane.x, tabPane.y);
        p.addPoint(paneRight, tabPane.y);
        // create the right-side
        p.addPoint(paneRight, paneBottom);

        // create the bottom
        p.addPoint(tabRight, paneBottom);
        p.addPoint(tabRight, tabBottom);
        p.addPoint(tab.x, tabBottom);

        // if the tab isn't all the way to the left, then create the path
        // until we reach the left-side
        if(tab.x!=0) {
            p.addPoint(tab.x, paneBottom);
            p.addPoint(tabPane.x, paneBottom);
        }

        return p;
    }

    protected boolean isOuterRegion(String region) {
        return NORTH_REGION.equals(region) || SOUTH_REGION.equals(region) ||
               EAST_REGION.equals(region) || WEST_REGION.equals(region);
    }

    public abstract void drawPreview(Graphics2D g, Polygon poly, Dockable dockable, Map dragInfo);
}
