/*
 * 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);
}
