package echopointng.ui.syncpeer;

/* 
 * This file is part of the Echo Point Project.  This project is a collection
 * of Components that have extended the Echo Web Application Framework.
 *
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 */

import nextapp.echo2.app.Alignment;
import nextapp.echo2.app.Border;
import nextapp.echo2.app.Color;
import nextapp.echo2.app.Component;
import nextapp.echo2.app.Extent;
import nextapp.echo2.app.ImageReference;
import nextapp.echo2.app.Insets;
import nextapp.echo2.app.Style;
import nextapp.echo2.app.update.ServerComponentUpdate;
import nextapp.echo2.webcontainer.ActionProcessor;
import nextapp.echo2.webcontainer.ComponentSynchronizePeer;
import nextapp.echo2.webcontainer.ContainerInstance;
import nextapp.echo2.webcontainer.DomUpdateSupport;
import nextapp.echo2.webcontainer.RenderContext;
import nextapp.echo2.webcontainer.SynchronizePeerFactory;
import nextapp.echo2.webcontainer.propertyrender.InsetsRender;

import org.w3c.dom.Element;
import org.w3c.dom.Node;

import echopointng.EPNG;
import echopointng.TabbedPane;
import echopointng.tabbedpane.TabImageRenderer;
import echopointng.tabbedpane.TabModel;
import echopointng.ui.resource.Resources;
import echopointng.ui.util.CssStyleEx;
import echopointng.ui.util.ImageManager;
import echopointng.ui.util.LayoutStrut;
import echopointng.ui.util.RenderingContext;
import echopointng.util.ColorKit;

/**
 * <code>TabbedPanePeer</code> is the peer for <code>TabbedPane</code>
 */
public class TabbedPanePeer extends AbstractEchoPointContainerPeer implements ActionProcessor {

	private final static String IMAGE_PREFIX = "tabIcon";

	private final static String LEADIN_IMAGE_PREFIX = "leadIn";

	private final static String LEADOUT_IMAGE_PREFIX = "leadOut";

	/**
	 * @see nextapp.echo2.webcontainer.ActionProcessor#processAction(nextapp.echo2.webcontainer.ContainerInstance,
	 *      nextapp.echo2.app.Component, org.w3c.dom.Element)
	 */
	public void processAction(ContainerInstance ci, Component component, Element actionElement) {
		String actionName = actionElement.getAttribute(ActionProcessor.ACTION_NAME);
		String actionValue = actionElement.getAttribute(ActionProcessor.ACTION_VALUE);

		ci.getUpdateManager().getClientUpdateManager().setComponentAction(component, actionName, actionValue);
	}

	/**
	 * Called to register and image with the ImageManager
	 */
	private void registerSingleImage(TabbedPane tp, int index, ImageManager iman) {
		if (tp.getModel().getTabImageRenderer() != null) {
			int selectedIndex = tp.getSelectedIndex();
			if (selectedIndex == -1)
				selectedIndex = 0;
			boolean isSelected = index == selectedIndex;

			Component tabComponent = tp.getModel().getTabAt(index, isSelected);
			ImageReference imageRef = tp.getModel().getTabImageRenderer().getTabImage(tp, index, tabComponent, isSelected);
			if (imageRef == null)
				throw new IllegalStateException("The TabImageRenderer must return a non null active image at index : " + index);

			iman.setImage(IMAGE_PREFIX + index, imageRef);
		}
	}

	/**
	 * Called to register all images with the ImageManager
	 */
	private void registerAllImages(TabbedPane tp, ImageManager iman) {
		int len = tp.size();
		for (int i = 0; i < len; i++) {
			registerSingleImage(tp, i, iman);
		}
		ImageReference lead;
		TabModel model = tp.getModel();
		lead = (model.getTabImageRenderer() == null ? null : model.getTabImageRenderer().getLeadInImage(tp));
		if (lead != null)
			iman.setImage(LEADIN_IMAGE_PREFIX, lead);
		lead = (model.getTabImageRenderer() == null ? null : model.getTabImageRenderer().getLeadOutImage(tp));
		if (lead != null)
			iman.setImage(LEADOUT_IMAGE_PREFIX, lead);
	}

	private String getBorderStyleString(int borderStyle) {
		switch (borderStyle) {
		case Border.STYLE_DASHED:
			return "dashed";
		case Border.STYLE_DOTTED:
			return "dotted";
		case Border.STYLE_DOUBLE:
			return "double";
		case Border.STYLE_GROOVE:
			return "groove";
		case Border.STYLE_INSET:
			return "inset";
		case Border.STYLE_NONE:
			return "none";
		case Border.STYLE_OUTSET:
			return "outset";
		case Border.STYLE_RIDGE:
			return "ridge";
		case Border.STYLE_SOLID:
			return "solid";
		default:
			return "";
		}
	}

	/**
	 * Sets the border Style based on all the border parameters
	 */
	private void _setBorderStyle(CssStyleEx style, Color borderColor, int borderStyle, int topWidth, int bottomWidth, int leftWidth, int rightWidth) {
		if (borderColor == null || leftWidth == 0 || rightWidth == 0)
			return;
		String bStyle = getBorderStyleString(borderStyle);
		String bColor = ColorKit.makeCSSColor(borderColor);

		style.setAttribute("border-left-width", String.valueOf(leftWidth) + "px");
		style.setAttribute("border-left-color", bColor);
		style.setAttribute("border-left-style", bStyle);

		style.setAttribute("border-right-width", String.valueOf(rightWidth) + "px");
		style.setAttribute("border-right-color", bColor);
		style.setAttribute("border-right-style", bStyle);

		style.setAttribute("border-top-width", String.valueOf(topWidth) + "px");
		style.setAttribute("border-top-color", bColor);
		style.setAttribute("border-top-style", bStyle);

		style.setAttribute("border-bottom-width", String.valueOf(bottomWidth) + "px");
		style.setAttribute("border-bottom-color", bColor);
		style.setAttribute("border-bottom-style", bStyle);
	}

	/**
	 * Sets the border properties if we have some border values, into the
	 * CssStyle.
	 */
	private void _setBorderStyleAttributes(CssStyleEx style, TabbedPane tp, int tabPlacement, Color borderColor, int borderWidth, int borderStyle) {
		if (borderColor == null || borderWidth < 0)
			return;

		if (tabPlacement == Alignment.BOTTOM) {
			_setBorderStyle(style, borderColor, borderStyle, /* T */borderWidth, /* B */0, /* L */borderWidth, /* R */borderWidth);
		} else {
			_setBorderStyle(style, borderColor, borderStyle, /* T */0, /* B */borderWidth, /* L */borderWidth, /* R */borderWidth);
		}
	}

	/**
	 * Returns a style for the active Tab TD
	 */
	private String _createTabActiveStyle(RenderingContext rc, TabbedPane tp, int tabPlacement, Color borderColor, int borderWidth, int borderStyle) {
		CssStyleEx style = new CssStyleEx();
		if (borderWidth > 0 && borderColor != null) {
			if (tabPlacement == Alignment.BOTTOM) {
				_setBorderStyle(style, borderColor, borderStyle, /* T */0, /* B */borderWidth, /* L */borderWidth, /* R */borderWidth);
			} else {
				_setBorderStyle(style, borderColor, borderStyle, /* T */borderWidth, /* B */0, /* L */borderWidth, /* R */borderWidth);
			}
		}
		return style.renderInline();
	}

	/**
	 * Returns a styleName for a normal non selected tab
	 */
	private String _createTabInactiveStyle(RenderingContext rc, TabbedPane tp, int tabPlacement, Color borderColor, int borderWidth, int borderStyle) {
		CssStyleEx style = new CssStyleEx();
		if (borderWidth > 0 && borderColor != null) {
			if (tabPlacement == Alignment.BOTTOM) {
				_setBorderStyle(style, borderColor, borderStyle, /* T */0, /* B */borderWidth, /* L */borderWidth, /* R */borderWidth);
			} else {
				_setBorderStyle(style, borderColor, borderStyle, /* T */borderWidth, /* B */0, /* L */borderWidth, /* R */borderWidth);
			}
		}
		return style.renderInline();
	}

	/**
	 * Returns a style name for an active Tab Line TD.
	 */
	private String _createTabActiveLineStyle(RenderingContext rc, TabbedPane tp, Color borderColor, int borderWidth, int borderStyle, Style fallbackStyle) {
		CssStyleEx style = new CssStyleEx();

		if (tp.getModel().getTabImageRenderer() != null) {
			borderWidth = tp.getModel().getTabImageRenderer().getImageBorderWidth();
			borderColor = tp.getModel().getTabImageRenderer().getImageBorderColor();
		}
		style.setAttribute("height", String.valueOf(borderWidth) + "px");

		if (borderColor != null && borderWidth > 0) {
			_setBorderStyle(style, borderColor, borderStyle, /* B */0, /* T */0, /* L */borderWidth, /* R */borderWidth);
		}
		Component tabComponent = tp.getModel().getTabAt(tp.getSelectedIndex(), true);
		Color background;
		if (tp.getModel().getTabImageRenderer() != null) {
			// 
			// if the TabbedPane has been primed right, it will
			// have a background color all set up to go with
			// the image renderer.
			//
			background = (Color) rc.getRP(TabbedPane.PROPERTY_BACKGROUND, fallbackStyle);
			style.setBackground(background);
		} else if (tabComponent != null) {
			//
			// make the background the same as the tab component so its bleeds
			// into the color of the tab TD
			//
			background = (Color) tabComponent.getRenderProperty(TabbedPane.PROPERTY_BACKGROUND);
			style.setBackground(background);
		}

		return style.renderInline();
	}

	/**
	 * Returns a style name for an inactive Tab Line TD.
	 */
	private String _createTabInActiveLineStyle(RenderingContext rc, TabbedPane tp, Color borderColor, int borderWidth, int borderStyle) {
		CssStyleEx style = new CssStyleEx();
		if (tp.getModel().getTabImageRenderer() != null) {
			borderColor = tp.getModel().getTabImageRenderer().getImageBorderColor();
		}
		style.setAttribute("height", String.valueOf(borderWidth) + "px");
		if (borderColor != null) {
			style.setBackground(borderColor);
		}
		return style.renderInline();
	}

	/**
	 * Draws a child component
	 */
	private void drawChild(RenderingContext rc, Component child, Element parent, String containerId) {
		ComponentSynchronizePeer syncPeer = SynchronizePeerFactory.getPeerForComponent(child.getClass());
		if (syncPeer instanceof DomUpdateSupport) {
			((DomUpdateSupport) syncPeer).renderHtml(rc, rc.getServerComponentUpdate(), parent, child);
		} else {
			syncPeer.renderAdd(rc, rc.getServerComponentUpdate(), containerId, child);
		}
	}

	/**
	 * Renders the actual contents of the TabbedPane
	 */
	private void renderComplexTabContents(RenderingContext rc, TabbedPane tp, Element parent, Color borderColor, int borderWidth, int borderStyle) {
		Style fallbackStyle = EPNG.getFallBackStyle(tp);

		TabImageRenderer renderer = tp.getModel().getTabImageRenderer();
		int index = tp.getSelectedIndex();
		Component tabContent = tp.getModel().getTabContentAt(index);
		Component tabComponent = tp.getModel().getTabAt(index, true);

		CssStyleEx style = new CssStyleEx();
		Color background = (Color)rc.getRP(TabbedPane.PROPERTY_BACKGROUND, fallbackStyle);
		if (renderer != null) {
			// 
			// if the TabbedPane has been primed right, it will
			// have a background color all set up to go with
			// the image renderer.
			//
			style.setBackground(background);
		} else if (tabComponent != null) {
			//
			// make the background the same as the tab component so its bleeds
			// into the color of the tab TD
			//
			background = (Color) tabComponent.getRenderProperty(TabbedPane.PROPERTY_BACKGROUND);
			style.setBackground(background);
		}
		int tabPlacement = rc.getRP(TabbedPane.PROPERTY_TAB_PLACEMENT,fallbackStyle, Alignment.TOP);
		
		_setBorderStyleAttributes(style, tp, tabPlacement, borderColor, borderWidth, borderStyle);

		InsetsRender.renderToStyle(style, "padding", (Insets) rc.getRP(TabbedPane.PROPERTY_INSETS, fallbackStyle));
		parent.setAttribute("style", style.renderInline());
		if (tabContent != null) {
			String contentContainerId = getContainerId(tabContent);
			parent.setAttribute("id", contentContainerId);
			drawChild(rc, tabContent, parent, contentContainerId);
		}
	}

	/**
	 * Renders a complex tab interface.
	 */
	private void renderComplexTabs(RenderingContext rc, TabbedPane tp, Node parentNode, ImageManager imageManager) {
		Style fallbackStyle = EPNG.getFallBackStyle(tp);
		TabImageRenderer renderer = tp.getModel().getTabImageRenderer();
		boolean tabImageOnly = (renderer != null);

		int tabCount = tp.size();
		int selectedIndex = tp.getSelectedIndex();

		Border border = (Border) rc.getRP(TabbedPane.PROPERTY_BORDER, fallbackStyle);
		Color borderColor = (border == null) ? null : border.getColor();
		int borderWidth = (border == null) ? 0 : (border.getSize() == null ? 0 : border.getSize().getValue());
		int borderStyle = (border == null) ? 0 : border.getStyle();
		if (renderer != null) {
			borderColor = renderer.getImageBorderColor();
			borderWidth = renderer.getImageBorderWidth();
		}

		int tabSpacing = rc.getRP(TabbedPane.PROPERTY_TAB_SPACING,fallbackStyle, 5);
		int tabPlacement = rc.getRP(TabbedPane.PROPERTY_TAB_PLACEMENT,fallbackStyle, Alignment.TOP);
		
		String tabLineInactiveStyle = _createTabInActiveLineStyle(rc, tp, borderColor, borderWidth, borderStyle);
		String tabLineActiveStyle = _createTabActiveLineStyle(rc, tp,  borderColor, borderWidth, borderStyle, fallbackStyle);
		String tabActiveTabTDBorderStyle = _createTabActiveStyle(rc, tp, tabPlacement, borderColor, borderWidth, borderStyle);
		String tabInActiveTabTDBorderStyle = _createTabInactiveStyle(rc, tp, tabPlacement, borderColor, borderWidth, borderStyle);

		Element td;
		Element imgE;
		ImageReference icon;

		// -------------------------------------
		// Tab Contents Creation
		// -------------------------------------
		Element trContents = rc.createE("tr");
		td = rc.createE("td");
		trContents.appendChild(td);
		renderComplexTabContents(rc, tp, td, borderColor, borderWidth, borderStyle);

		// -------------------------------------
		// Tab Item Creation
		// -------------------------------------
		Element trTabItems = rc.createE("tr");
		td = rc.createE("td");
		trTabItems.appendChild(td);

		//
		// table for the tab items themselves
		Element tableTabs = rc.createE("table");
		td.appendChild(tableTabs);
		tableTabs.setAttribute("border", "0");
		tableTabs.setAttribute("cellpadding", "0");
		tableTabs.setAttribute("cellspacing", "0");
		tableTabs.setAttribute("width", "100%");

		Element tbodyTabs = rc.createE("tbody");
		tableTabs.appendChild(tbodyTabs);

		Element trItems = rc.createE("tr");

		//
		// Lead in Image if available
		ImageReference leadInIcon = imageManager.getImage(LEADIN_IMAGE_PREFIX);
		icon = leadInIcon;
		if (icon != null) {
			td = rc.createE("td");
			trItems.appendChild(td);
			td.setAttribute("valign", tabPlacement == Alignment.TOP ? "bottom" : "top");

			imgE = ImageManager.createImgE(rc,null,icon);
			td.appendChild(imgE);
		}

		for (int index = 0; index < tabCount; index++) {
			boolean isSelected = (selectedIndex == index);

			// -------------------------------------
			// Tab Cell Creation
			// -------------------------------------

			td = rc.createE("td");
			trItems.appendChild(td);

			Component tabComponent = tp.getModel().getTabAt(index, isSelected);
			String tabCellId = getContainerId(tabComponent);
			if (tabImageOnly) {
				//
				// from the tabimagerenderer icon handling
				String imagePrefix = IMAGE_PREFIX + index;
				icon = imageManager.getImage(imagePrefix);
				
				imgE = ImageManager.createImgE(rc,null,icon);

				Element anchorE = rc.createE("a");
				if (tp.isRenderEnabled()) {
					anchorE.setAttribute("href", "javascript:EP.Event.hrefActionHandler('" + rc.getElementId() + "','click','"
							+ String.valueOf(index) + "')");
				} else {
					anchorE.setAttribute("href", "javascript:void");
				}
				anchorE.appendChild(imgE);
				td.appendChild(anchorE);

				// if we are a tab as an image, then
				Element noDisplayE = rc.createE("bdo");
				noDisplayE.setAttribute("id", tabCellId);
				noDisplayE.setAttribute("style", "display:none");
				td.appendChild(noDisplayE);
			} else {
				//
				// we have a child component that needs rendering
				td.setAttribute("id", tabCellId);
				drawChild(rc, tabComponent, td, tabCellId);
			}
			//
			// does it require a style inside the TD
			if (!tabImageOnly) {
				if (isSelected) {
					td.setAttribute("style", tabActiveTabTDBorderStyle);
				} else {
					td.setAttribute("style", tabInActiveTabTDBorderStyle);
				}
			}
			td.setAttribute("align", "center");
			if (tabPlacement == Alignment.TOP) {
				td.setAttribute("valign", "bottom");
			} else {
				td.setAttribute("valign", "top");
			}
			//
			// spacer please
			if (index < tabCount - 1) {
				td = rc.createE("td");
				trItems.appendChild(td);
				td.appendChild(LayoutStrut.createStrut(rc, tabSpacing, 1));
			}
		}

		//
		// Lead Out Image if available
		ImageReference leadOutIcon = imageManager.getImage(LEADOUT_IMAGE_PREFIX);
		icon = leadOutIcon;
		if (icon != null) {
			td = rc.createE("td");
			trItems.appendChild(td);
			td.setAttribute("valign", tabPlacement == Alignment.TOP ? "bottom" : "top");

			imgE = ImageManager.createImgE(rc,null,icon);
			td.appendChild(imgE);
		}

		//
		// wide TD to force a spring fill of the rest of the box
		td = rc.createE("td");
		trItems.appendChild(td);
		td.setAttribute("width", "100%");
		td.appendChild(LayoutStrut.createStrut(rc, 1, 1));

		// -------------------------------------
		// Tab Line Row Creation
		// -------------------------------------
		if (borderWidth > 0 && borderColor != null) {
			Element trLines = rc.createE("tr");

			int colspanPrev = selectedIndex * 2;
			int colspanPost = ((tabCount - selectedIndex) * 2);
			if (leadInIcon != null)
				colspanPrev += 1;
			if (leadOutIcon != null)
				colspanPost += 1;
			if (selectedIndex > 0 || leadInIcon != null) {
				// inactive tab pre part
				td = rc.createE("td");
				trLines.appendChild(td);
				td.setAttribute("style", tabLineInactiveStyle);
				td.setAttribute("colspan", "" + colspanPrev);
				//td.appendChild(LayoutStrut.createStrut(rc, 1, borderWidth));
			}
			// active tab part
			td = rc.createE("td");
			trLines.appendChild(td);
			td.setAttribute("style", tabLineActiveStyle);
			td.setAttribute("colspan", "1");
			//td.appendChild(LayoutStrut.createStrut(rc, 1, borderWidth));

			// inactive tab post part
			td = rc.createE("td");
			trLines.appendChild(td);
			td.setAttribute("style", tabLineInactiveStyle);
			td.setAttribute("colspan", "" + colspanPost);
			//td.appendChild(LayoutStrut.createStrut(rc, 1, borderWidth));

			if (tabPlacement == Alignment.TOP) {
				tbodyTabs.appendChild(trItems);
				tbodyTabs.appendChild(trLines);
			} else {
				tbodyTabs.appendChild(trLines);
				tbodyTabs.appendChild(trItems);
			}
		} else {
			tbodyTabs.appendChild(trItems);
		}

		// -------------------------------------
		// Tab Outer Table Creation
		// -------------------------------------
		Element table = rc.createE("table");
		table.setAttribute("border", "0");
		table.setAttribute("cellpadding", "0");
		table.setAttribute("cellspacing", "0");
		
		Extent width = (Extent) rc.getRP(TabbedPane.PROPERTY_WIDTH, fallbackStyle);
		if (width != null) {
			tableTabs.setAttribute("width", width.toString());
		} else {
			table.setAttribute("width", "100%");
		}
		Element tbody = rc.createE("tbody");
		table.appendChild(tbody);

		if (tabPlacement == Alignment.TOP) {
			tbody.appendChild(trTabItems);
			tbody.appendChild(trContents);
		} else {
			tbody.appendChild(trContents);
			tbody.appendChild(trTabItems);
		}
		//
		// out outer most element is a DIV
		Element div = rc.createE("div");
		CssStyleEx style = new CssStyleEx();
		InsetsRender.renderToStyle(style, "margin", (Insets) rc.getRP(TabbedPane.PROPERTY_OUTSETS, fallbackStyle));

		div.setAttribute("style", style.renderInline());
		div.setAttribute("id", rc.getElementId());
		rc.addStandardWebSupport(div);
		div.appendChild(table);
		parentNode.appendChild(div);
	}

	/**
	 * @see echopointng.ui.syncpeer.AbstractEchoPointPeer#renderHtml(echopointng.ui.util.RenderingContext,
	 *      Node, nextapp.echo2.app.Component)
	 */
	public void renderHtml(RenderingContext rc, Node parent, Component component) {
		TabbedPane tp = (TabbedPane) component;
		
		rc.addLibrary(Resources.EP_SCRIPT_SERVICE);
		
		ImageManager imageManager = (ImageManager) retreiveRenderState(component);
		if (imageManager == null) {
			imageManager = new ImageManager();
			storeRenderState(component,imageManager);
		}
		registerAllImages(tp, imageManager);
		renderComplexTabs(rc, tp, parent, imageManager);
	}

	/**
	 * @see echopointng.ui.syncpeer.AbstractEchoPointPeer#renderUpdate(nextapp.echo2.webcontainer.RenderContext,
	 *      nextapp.echo2.app.update.ServerComponentUpdate, java.lang.String)
	 */
	public boolean renderUpdate(RenderContext rc, ServerComponentUpdate update, String targetId) {
		return renderUpdateBaseImpl(rc, update, targetId, true);
	}

}
