/**
 * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Liferay Enterprise
 * Subscription License ("License"). You may not use this file except in
 * compliance with the License. You can obtain a copy of the License by
 * contacting Liferay, Inc. See the License for the specific language governing
 * permissions and limitations under the License, including but not limited to
 * distribution rights of the Software.
 *
 *
 *
 */

package com.liferay.taglib.portletext;

import com.liferay.portal.kernel.json.JSONFactoryUtil;
import com.liferay.portal.kernel.json.JSONObject;
import com.liferay.portal.kernel.language.LanguageUtil;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.portlet.PortletContainerUtil;
import com.liferay.portal.kernel.portlet.PortletJSONUtil;
import com.liferay.portal.kernel.portlet.PortletLayoutListener;
import com.liferay.portal.kernel.portlet.PortletParameterUtil;
import com.liferay.portal.kernel.portlet.RestrictPortletServletRequest;
import com.liferay.portal.kernel.servlet.DynamicServletRequest;
import com.liferay.portal.kernel.servlet.PipingServletResponse;
import com.liferay.portal.kernel.servlet.PortalIncludeUtil;
import com.liferay.portal.kernel.util.AutoResetThreadLocal;
import com.liferay.portal.kernel.util.MapUtil;
import com.liferay.portal.kernel.util.PrefixPredicateFilter;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.WebKeys;
import com.liferay.portal.model.Layout;
import com.liferay.portal.model.LayoutTypePortlet;
import com.liferay.portal.model.Portlet;
import com.liferay.portal.service.PortletLocalServiceUtil;
import com.liferay.portal.service.PortletPreferencesLocalServiceUtil;
import com.liferay.portal.theme.ThemeDisplay;
import com.liferay.portal.util.PortalUtil;
import com.liferay.portal.util.PortletKeys;
import com.liferay.portlet.PortletPreferencesFactoryUtil;

import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;

/**
 * @author Brian Wing Shun Chan
 */
public class RuntimeTag extends TagSupport {

	public static void doTag(
			String portletName, PageContext pageContext,
			HttpServletRequest request, HttpServletResponse response)
		throws Exception {

		doTag(portletName, null, pageContext, request, response);
	}

	public static void doTag(
			String portletName, String queryString, PageContext pageContext,
			HttpServletRequest request, HttpServletResponse response)
		throws Exception {

		doTag(portletName, queryString, null, pageContext, request, response);
	}

	public static void doTag(
			String portletName, String queryString, String defaultPreferences,
			PageContext pageContext, HttpServletRequest request,
			HttpServletResponse response)
		throws Exception {

		if (pageContext != null) {
			response = new PipingServletResponse(
				response, pageContext.getOut());
		}

		String portletId = portletName;

		HttpServletRequest originalRequest =
			PortalUtil.getOriginalServletRequest(request);

		RestrictPortletServletRequest restrictPortletServletRequest =
			new RestrictPortletServletRequest(originalRequest);

		queryString = PortletParameterUtil.addNamespace(portletId, queryString);

		Map<String, String[]> parameterMap = new HashMap<String, String[]>(
			request.getParameterMap());

		if (!portletId.equals(request.getParameter("p_p_id"))) {
			parameterMap = MapUtil.filter(
				parameterMap, new HashMap<String, String[]>(),
				new PrefixPredicateFilter("p_p_"));
		}

		String portletNamespace = PortalUtil.getPortletNamespace(portletId);

		parameterMap.putAll(
			MapUtil.filter(
				originalRequest.getParameterMap(),
				new HashMap<String, String[]>(),
				new PrefixPredicateFilter(portletNamespace, true)));

		request = DynamicServletRequest.addQueryString(
			restrictPortletServletRequest, parameterMap, queryString, false);

		try {
			request.setAttribute(WebKeys.RENDER_PORTLET_RESOURCE, Boolean.TRUE);

			ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
				WebKeys.THEME_DISPLAY);

			Stack<String> embeddedPortletIds = _embeddedPortletIds.get();

			if (embeddedPortletIds == null) {
				embeddedPortletIds = new Stack<String>();

				_embeddedPortletIds.set(embeddedPortletIds);
			}

			if (embeddedPortletIds.search(portletId) > -1) {
				String errorMessage = LanguageUtil.get(
					pageContext, "the-application-cannot-include-itself");

				request.setAttribute(
					"liferay-portlet:runtime:errorMessage", errorMessage);

				PortalIncludeUtil.include(pageContext, _ERROR_PAGE);

				return;
			}

			if (themeDisplay.isStateMaximized()) {
				LayoutTypePortlet layoutTypePortlet =
					themeDisplay.getLayoutTypePortlet();

				if (layoutTypePortlet.hasStateMaxPortletId(portletId)) {

					// A portlet in the maximized state has already been
					// processed

					return;
				}
			}

			Layout layout = themeDisplay.getLayout();

			Portlet portlet = getPortlet(
				themeDisplay.getCompanyId(), portletId);

			JSONObject jsonObject = JSONFactoryUtil.createJSONObject();

			if ((PortletPreferencesLocalServiceUtil.getPortletPreferencesCount(
					PortletKeys.PREFS_OWNER_TYPE_LAYOUT, themeDisplay.getPlid(),
					portletId) < 1) ||
				layout.isTypeControlPanel() ||
				layout.isTypePanel()) {

				PortletPreferencesFactoryUtil.getLayoutPortletSetup(
					layout, portletId, defaultPreferences);
				PortletPreferencesFactoryUtil.getPortletSetup(
					request, portletId, defaultPreferences);

				PortletLayoutListener portletLayoutListener =
					portlet.getPortletLayoutListenerInstance();

				if (portletLayoutListener != null) {
					portletLayoutListener.onAddToLayout(
						portletId, themeDisplay.getPlid());
				}

				PortletJSONUtil.populatePortletJSONObject(
					request, StringPool.BLANK, portlet, jsonObject);
			}

			PortletJSONUtil.writeHeaderPaths(response, jsonObject);

			embeddedPortletIds.push(portletId);

			PortletContainerUtil.render(request, response, portlet);

			embeddedPortletIds.pop();

			PortletJSONUtil.writeFooterPaths(response, jsonObject);
		}
		finally {
			restrictPortletServletRequest.mergeSharedAttributes();
		}
	}

	@Override
	public int doEndTag() throws JspException {
		try {
			HttpServletRequest request =
				(HttpServletRequest)pageContext.getRequest();

			Layout layout = (Layout)request.getAttribute(WebKeys.LAYOUT);

			if (layout == null) {
				return EVAL_PAGE;
			}

			HttpServletResponse response =
				(HttpServletResponse)pageContext.getResponse();

			doTag(
				_portletName, _queryString, _defaultPreferences, pageContext,
				request, response);

			return EVAL_PAGE;
		}
		catch (Exception e) {
			_log.error(e, e);

			throw new JspException(e);
		}
	}

	public void setDefaultPreferences(String defaultPreferences) {
		_defaultPreferences = defaultPreferences;
	}

	public void setPortletName(String portletName) {
		_portletName = portletName;
	}

	public void setQueryString(String queryString) {
		_queryString = queryString;
	}

	/**
	 * @see com.liferay.portal.model.impl.LayoutTypePortletImpl#getStaticPortlets(
	 *      String)
	 */
	protected static Portlet getPortlet(long companyId, String portletId)
		throws Exception {

		Portlet portlet = PortletLocalServiceUtil.getPortletById(
			companyId, portletId);

		// See LayoutTypePortletImpl#getStaticPortlets for why we only clone
		// non-instanceable portlets

		if (!portlet.isInstanceable()) {
			portlet = (Portlet)portlet.clone();
		}

		portlet.setStatic(true);

		return portlet;
	}

	private static final String _ERROR_PAGE =
		"/html/taglib/portlet/runtime/error.jsp";

	private static Log _log = LogFactoryUtil.getLog(RuntimeTag.class);

	private static final ThreadLocal<Stack<String>> _embeddedPortletIds =
		new AutoResetThreadLocal<Stack<String>>(
			RuntimeTag.class + "._embeddedPortletIds");

	private String _defaultPreferences;
	private String _portletName;
	private String _queryString;

}