/**
 * 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.portal.util;

import com.liferay.portal.kernel.cache.Lifecycle;
import com.liferay.portal.kernel.cache.ThreadLocalCache;
import com.liferay.portal.kernel.cache.ThreadLocalCacheManager;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.util.LocaleUtil;
import com.liferay.portal.kernel.util.ObjectValuePair;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.model.Layout;
import com.liferay.portal.model.LayoutConstants;
import com.liferay.portal.model.impl.LayoutImpl;
import com.liferay.portal.service.LayoutLocalServiceUtil;

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;

/**
 * @author Brian Wing Shun Chan
 * @author Shuyang Zhou
 */
public class LayoutLister {

	public List<LayoutDescription> getLayoutDescriptions(
			long groupId, boolean privateLayout, String rootNodeName,
			Locale locale)
		throws PortalException, SystemException {

		ThreadLocalCache<List<LayoutDescription>> threadLocalCache =
			ThreadLocalCacheManager.getThreadLocalCache(
				Lifecycle.REQUEST, LayoutLister.class.getName());

		String cacheKey = buildCacheKey(
			groupId, privateLayout, rootNodeName, locale);

		List<LayoutDescription> layoutDescriptions = threadLocalCache.get(
			cacheKey);

		if (layoutDescriptions == null) {
			layoutDescriptions = doGetLayoutDescriptions(
				groupId, privateLayout, rootNodeName, locale);

			threadLocalCache.put(cacheKey, layoutDescriptions);
		}

		return layoutDescriptions;
	}

	protected static String buildCacheKey(
		long groupId, boolean privateLayout, String rootNodeName,
		Locale locale) {

		StringBundler sb = new StringBundler(7);

		sb.append(StringUtil.toHexString(groupId));
		sb.append(StringPool.POUND);
		sb.append(privateLayout);
		sb.append(StringPool.POUND);
		sb.append(rootNodeName);
		sb.append(StringPool.POUND);
		sb.append(LocaleUtil.toLanguageId(locale));

		return sb.toString();
	}

	protected static List<LayoutDescription> doGetLayoutDescriptions(
			long groupId, boolean privateLayout, String rootNodeName,
			Locale locale)
		throws PortalException, SystemException {

		List<LayoutDescription> layoutDescriptions =
			new ArrayList<LayoutDescription>();

		List<Layout> layouts = new ArrayList<Layout>(
			LayoutLocalServiceUtil.getLayouts(groupId, privateLayout));

		Deque<ObjectValuePair<Layout, Integer>> deque =
			new LinkedList<ObjectValuePair<Layout, Integer>>();

		Layout rootLayout = new LayoutImpl();

		rootLayout.setPlid(LayoutConstants.DEFAULT_PLID);
		rootLayout.setName(rootNodeName);

		deque.push(new ObjectValuePair<Layout, Integer>(rootLayout, 0));

		ObjectValuePair<Layout, Integer> objectValuePair = null;

		while ((objectValuePair = deque.pollFirst()) != null) {
			Layout currentLayout = objectValuePair.getKey();

			Integer currentDepth = objectValuePair.getValue();

			layoutDescriptions.add(
				new LayoutDescription(
					currentLayout.getPlid(), currentLayout.getName(locale),
					currentDepth));

			ListIterator<Layout> listIterator = layouts.listIterator(
				layouts.size());

			while (listIterator.hasPrevious()) {
				Layout previousLayout = listIterator.previous();

				if (previousLayout.getParentLayoutId() ==
						currentLayout.getLayoutId()) {

					listIterator.remove();

					deque.push(
						new ObjectValuePair<Layout, Integer>(
							previousLayout, currentDepth + 1));
				}
			}
		}

		return layoutDescriptions;
	}

}