package geogebra.gui.util;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.SystemColor;

import javax.swing.Icon;
import javax.swing.JTabbedPane;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.plaf.TabbedPaneUI;
import javax.swing.plaf.basic.BasicTabbedPaneUI;

/**
 * Tabbed pane with icons and a minimalistic appearance.
 * 
 * This tabbed pane will just use the special IconTabbedPaneUI class for
 * it's UI. It's required to reconstruct the UI object if updateUI() is
 * called (e.g. because the font size changed).
 * 
 * @author Florian Sonner
 */
public class IconTabbedPane extends JTabbedPane {
	/** */
	private static final long serialVersionUID = 1L;
	
	/**
	 * Set the UI of this component to the IconTabbedPaneUI.
	 */
	public IconTabbedPane() {
		setUI(new IconTabbedPaneUI());
	}
	
	/**
	 * Ignore any non IconTabbedPaneUI objects.
	 */
	public void setUI(TabbedPaneUI ui) {
		if(ui instanceof IconTabbedPaneUI) {
			super.setUI(ui);
		}
	}
	
	/**
	 * Update the UI of this component.
	 * 
	 * This will lead to an update of the fonts of the UI as just the font size
	 * should change.
	 */
	public void updateUI() {
		if(ui instanceof IconTabbedPaneUI) {
			((IconTabbedPaneUI)getUI()).updateFont();
		}
	}

	/**
	 * Custom UI for the tabbed pane with icons.
	 * 
	 * @author Florian Sonner
	 */
	class IconTabbedPaneUI extends BasicTabbedPaneUI {
		/**
		 * The background color for tabs which are neither active not hovered.
		 */
		private Color bgColor;
		
		/**
		 * The background color of active tabs (i.e. the content of this tab is currently
		 * displayed).
		 */
		private Color bgActiveColor;
		
		/**
		 * The background color of tabs the mouse is over at the moment. Will not apply
		 * to active tabs.
		 */
		private Color bgHoverColor;
	
		/**
		 * Initialization of default values.
		 */
		protected void installDefaults() {
			super.installDefaults();
			tabAreaInsets = new Insets(0, 15, 0, 15);
			contentBorderInsets = new Insets(3, 3, 3, 3);
			tabInsets = new Insets(10, 10, 10, 10);
			selectedTabPadInsets = new Insets(0, 0, 0, 0);
			
			bgColor = Color.white;
			bgActiveColor = new Color(193, 210, 238);
			bgHoverColor = new Color(224, 232, 246);
		}
		
		/**
		 * Uninstall our custom defaults.
		 */
		protected void uninstallDefaults() {
			super.uninstallDefaults();
			
			bgColor = null;
			bgActiveColor = null;
			bgHoverColor = null;
		}
		
		/**
		 * Update the font.
		 */
		public void updateFont() {
			LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background",
	                  "TabbedPane.foreground",
	                  "TabbedPane.font");
		}
		
		/**
		 * Paint the tab border.
		 */
		protected void paintTabBorder(Graphics g, int tabPlacement,
				int tabIndex, int x, int y, int w, int h, boolean isSelected) {
			g.setColor(SystemColor.controlShadow);
			g.drawLine(x, y, x, y + h - 1);
			g.drawLine(x + w, y, x + w, y + h - 1);
		}
	
		/**
		 * Paint the background of the tabs.
		 */
		protected void paintTabBackground(Graphics g, int tabPlacement,
				int tabIndex, int x, int y, int w, int h, boolean isSelected) {
			g.setColor(isSelected ? bgActiveColor : (tabIndex == getRolloverTab() ? bgHoverColor : bgColor));
			g.fillRect(x, y, w, h);
		}
		
		/**
		 * Repaint the tabbed pane if the mouse is hovering a new tab.
		 */
		protected void setRolloverTab(int index) {
			if(getRolloverTab() != index) {
				super.setRolloverTab(index);
				repaint();
			}
		}
	
		/**
		 * Fill the background with white.
		 */
		protected void paintTabArea(Graphics g, int tabPlacement,
				int selectedIndex) {
			g.setColor(Color.white);
			
			g.fillRect(0, 0,
				tabPane.getBounds().width,
				calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)
			);
	
			super.paintTabArea(g, tabPlacement, selectedIndex);
		}
	
		/**
		 * Use a custom layout for the label (icon centered, text below icon).
		 * 
		 * Copy 'n' paste from the original source of BasicTabbedPaneUI. 
		 */
		protected void layoutLabel(int tabPlacement, FontMetrics metrics,
				int tabIndex, String title, Icon icon, Rectangle tabRect,
				Rectangle iconRect, Rectangle textRect, boolean isSelected) {
			textRect.x = 0;
			textRect.y = 0;
			textRect.width = 0;
			textRect.height = 0;
			iconRect.x = 0;
			iconRect.y = 0;
			iconRect.width = 0;
			iconRect.height = 0;
			
			// -- just this has to be changed to change the layout of the tabs --
			SwingUtilities.layoutCompoundLabel(tabPane, metrics, title, icon,
					SwingConstants.CENTER, SwingConstants.CENTER,
					SwingConstants.BOTTOM, SwingConstants.CENTER, tabRect,
					iconRect, textRect, textIconGap);
	
			int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
			int shiftY = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
	
			iconRect.x += shiftX;
			iconRect.y += shiftY;
	
			textRect.x += shiftX;
			textRect.y += shiftY;
		}
	
		/**
		 * The tab should always have enough space for a 32x32 icon and the
		 * label.
		 */
		protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) {
			if(!isEnabledAt(tabIndex))
				return 0;
			
			return fontHeight + 45;
		}
	
		/**
		 * Reduce the tab width by 32 as the icon is not drawn in one line with
		 * the text.
		 */
		protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
			if(!isEnabledAt(tabIndex))
				return 0;
			
			return super.calculateTabWidth(tabPlacement, tabIndex, metrics) - 32;
		}
	
		/**
		 * Do not move the label if we select a tab (always return 0 as shift).
		 */
		protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
			return 0;
		}
	
		/**
		 * Paint the top border.
		 */
		protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
			g.setColor(SystemColor.controlDkShadow);
			g.drawLine(x, y, x + w, y);
			g.setColor(SystemColor.controlLtHighlight);
			g.drawLine(x, y + 1, x + w, y + 1);
		}
	
		protected void paintFocusIndicator(Graphics g, int tabPlacement,
				Rectangle[] rects, int tabIndex, Rectangle iconRect,
				Rectangle textRect, boolean isSelected) {
			/* paint nothing.. */
		}
	
		protected void paintContentBorderRightEdge(Graphics g,
				int tabPlacement, int selectedIndex, int x, int y, int w, int h) { 
			/* paint nothing */
		}
	
		protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
				int selectedIndex, int x, int y, int w, int h) {
			/* paint nothing */
		}
	
		protected void paintContentBorderBottomEdge(Graphics g,
				int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
			/* paint nothing */
		}
	}
}