/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.wicket.core.request.mapper;

import java.util.List;
import org.apache.wicket.core.request.handler.ListenerRequestHandler;
import org.apache.wicket.core.request.handler.PageAndComponentProvider;
import org.apache.wicket.core.request.handler.PageProvider;
import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.Url;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.mapper.info.ComponentInfo;
import org.apache.wicket.request.mapper.info.PageComponentInfo;
import org.apache.wicket.request.mapper.info.PageInfo;

/**
 * Decodes and encodes the following URLs:
 *
 * <pre>
 *  Page Instance - Render (RenderPageRequestHandler)
 *  /wicket/page?2
 *
 *
 *  Page Instance - Listener (ListenerRequestHandler)
 *  /wicket/page?2-click-foo-bar-baz
 *  /wicket/page?2-click.1-foo-bar-baz (1 is behavior index)
 * </pre>
 *
 * @author Matej Knopp
 */
public class PageInstanceMapper extends AbstractComponentMapper
{
	/**
	 * Construct.
	 */
	public PageInstanceMapper()
	{
	}

	/**
	 * @see org.apache.wicket.request.IRequestMapper#mapRequest(org.apache.wicket.request.Request)
	 */
	@Override
	public IRequestHandler mapRequest(Request request)
	{
		if (matches(request))
		{
			Url url = request.getUrl();
			PageComponentInfo info = getPageComponentInfo(url);
			if (info != null && info.getPageInfo().getPageId() != null)
			{
				Integer renderCount = info.getComponentInfo() != null ? info.getComponentInfo()
					.getRenderCount() : null;

				if (info.getComponentInfo() == null)
				{
					PageProvider provider = new PageProvider(info.getPageInfo().getPageId(),
						renderCount);
					provider.setPageSource(getContext());
					// render page
					return new RenderPageRequestHandler(provider);
				}
				else
				{
					ComponentInfo componentInfo = info.getComponentInfo();

					PageAndComponentProvider provider = new PageAndComponentProvider(
						info.getPageInfo().getPageId(), renderCount,
						componentInfo.getComponentPath());

					provider.setPageSource(getContext());

					return new ListenerRequestHandler(provider, componentInfo.getBehaviorId());
				}
			}
		}
		return null;
	}

	/**
	 * @see org.apache.wicket.request.IRequestMapper#mapHandler(org.apache.wicket.request.IRequestHandler)
	 */
	@Override
	public Url mapHandler(IRequestHandler requestHandler)
	{
		PageComponentInfo info = null;

		if (requestHandler instanceof RenderPageRequestHandler)
		{
			IRequestablePage page = ((RenderPageRequestHandler)requestHandler).getPage();

			PageInfo i = new PageInfo(page.getPageId());
			info = new PageComponentInfo(i, null);
		}
		else if (requestHandler instanceof ListenerRequestHandler)
		{
			ListenerRequestHandler handler = (ListenerRequestHandler)requestHandler;
			IRequestablePage page = handler.getPage();
			String componentPath = handler.getComponentPath();

			Integer renderCount = null;
			if (handler.includeRenderCount())
			{
				renderCount = page.getRenderCount();
			}

			PageInfo pageInfo = new PageInfo(page.getPageId());
			ComponentInfo componentInfo = new ComponentInfo(renderCount, componentPath, handler.getBehaviorIndex());
			info = new PageComponentInfo(pageInfo, componentInfo);
		}

		if (info != null)
		{
			Url url = new Url();
			url.getSegments().add(getContext().getNamespace());
			url.getSegments().add(getContext().getPageIdentifier());
			encodePageComponentInfo(url, info);
			return url;
		}
		else
		{
			return null;
		}
	}

	/**
	 * @see org.apache.wicket.request.IRequestMapper#getCompatibilityScore(org.apache.wicket.request.Request)
	 */
	@Override
	public int getCompatibilityScore(final Request request)
	{
		int score = 0;
		if (matches(request))
		{
			score = Integer.MAX_VALUE;
		}
		return score;
	}

	/**
	 * Matches when the request url starts with
	 * {@link org.apache.wicket.core.request.mapper.IMapperContext#getNamespace()}/{@link org.apache.wicket.core.request.mapper.IMapperContext#getPageIdentifier()}
	 * or when the base url starts with
	 * {@link org.apache.wicket.core.request.mapper.IMapperContext#getNamespace()}/{@link org.apache.wicket.core.request.mapper.IMapperContext#getPageIdentifier()}
	 * and the request url with {@link org.apache.wicket.core.request.mapper.IMapperContext#getPageIdentifier()}

	 * @param request
	 *      the request to check
	 * @return {@code true} if the conditions match
	 */
	private boolean matches(final Request request)
	{
		boolean matches = false;
		Url url = request.getUrl();
		Url baseUrl = request.getClientUrl();
		String namespace = getContext().getNamespace();
		String pageIdentifier = getContext().getPageIdentifier();
		List<String> segments = url.getSegments();
		
		if (isNotPageInstanceUrl(segments, pageIdentifier))
		{
			return false;
		}
		
		if (urlStartsWith(url, namespace, pageIdentifier))
		{
			matches = true;
		}
		else if (urlStartsWith(baseUrl, namespace, pageIdentifier) && urlStartsWith(url, pageIdentifier))
		{
			matches = true;
		}
		else if (urlStartsWith(baseUrl, pageIdentifier) && urlStartsWith(url, pageIdentifier))
		{
			matches = true;
		}

		return matches;
	}


	private boolean isNotPageInstanceUrl(List<String> segments, String pageIdentifier) 
	{		
		if (segments.size() > 2 ) 
		{
			return true;
		} 
		
		int pageIdIndex = segments.indexOf(pageIdentifier);
		
		//check if we have segments after pageIdentifier
		return pageIdIndex < 0 || segments.size() - 1 > pageIdIndex;
	}
}
