/*
 * 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.markup.html.link;

import org.apache.wicket.IRequestListener;
import org.apache.wicket.Page;
import org.apache.wicket.core.request.handler.IPageProvider;
import org.apache.wicket.core.request.handler.PageProvider;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.request.mapper.parameter.PageParameters;

/**
 * Implementation of an <a href="http://www.w3.org/TR/REC-html40/present/frames.html#h-16.5">inline
 * frame</a> component. Must be used with an iframe (&lt;iframe src...) element. The src attribute
 * will be generated.
 * 
 * @author Sven Meier
 * @author Ralf Ebert
 * 
 */

public class InlineFrame extends WebMarkupContainer implements IRequestListener
{
	private static final long serialVersionUID = 1L;

	/** The provider of the page. */
	private final IPageProvider pageProvider;

	/**
	 * Constructs an inline frame that instantiates the given Page class when the content of the
	 * inline frame is requested. The instantiated Page is used to render a response to the user.
	 * 
	 * @param <C>
	 * 
	 * @param id
	 *            See Component
	 * @param c
	 *            Page class
	 */
	public <C extends Page> InlineFrame(final String id, final Class<C> c)
	{
		this(id, c, null);
	}

	/**
	 * Constructs an inline frame that instantiates the given Page class when the content of the
	 * inline frame is requested. The instantiated Page is used to render a response to the user.
	 * 
	 * @param <C>
	 * 
	 * @param id
	 *            See Component
	 * @param c
	 *            Page class
	 * @param params
	 *            Page parameters
	 */
	public <C extends Page> InlineFrame(final String id, final Class<C> c,
		final PageParameters params)
	{
		this(id, new PageProvider(c, params));

		// Ensure that c is a subclass of Page
		if (!Page.class.isAssignableFrom(c))
		{
			throw new IllegalArgumentException("Class " + c + " is not a subclass of Page");
		}
	}

	/**
	 * This constructor is ideal if a Page object was passed in from a previous Page. Construct an
	 * inline frame containing the given Page.
	 * 
	 * @param id
	 *            See component
	 * @param page
	 *            The page
	 */
	public InlineFrame(final String id, final Page page)
	{
		this(id, new PageProvider(page.getPageId(), page.getClass(), page.getRenderCount()));
	}

	/**
	 * This constructor is ideal for constructing pages lazily.
	 * 
	 * Constructs an inline frame which invokes the getPage() method of the IPageLink interface when
	 * the content of the inline frame is requested. Whatever Page objects is returned by this
	 * method will be rendered back to the user.
	 * 
	 * @param id
	 *            See Component
	 * @param pageProvider
	 *            the provider of the page to be contained in the inline frame if and when the
	 *            content is requested
	 */
	public InlineFrame(final String id, IPageProvider pageProvider)
	{
		super(id);

		this.pageProvider = pageProvider;
	}

	/**
	 * Gets the url to use for this link.
	 * 
	 * @return The URL that this link links to
	 */
	protected CharSequence getURL()
	{
		return urlForListener(new PageParameters());
	}

	/**
	 * Handles this frame's tag.
	 * 
	 * @param tag
	 *            the component tag
	 * @see org.apache.wicket.Component#onComponentTag(ComponentTag)
	 */
	@Override
	protected void onComponentTag(final ComponentTag tag)
	{
		checkComponentTag(tag, "iframe");

		// Set href to link to this frame's frameRequested method
		CharSequence url = getURL();

		// generate the src attribute
		tag.put("src", url);

		super.onComponentTag(tag);
	}

	@Override
	public boolean rendersPage()
	{
		return false;
	}

	@Override
	public final void onRequest()
	{
		setResponsePage(pageProvider.getPageInstance());
	}


	@Override
	protected boolean getStatelessHint()
	{
		/*
		 * TODO optimization: the inlineframe component does not always have to be stateless.
		 * 
		 * unfortunately due to current implementation of using a ILinkListener
		 * callback it has to always be stateful because it can be put inside a ListView item which
		 * will not be built upon a stateless callback causing a "component at path
		 * listview:0:iframe not found" error.
		 * 
		 * eventually variant such as (string, ipagemap, class<? extends Page>) can be made
		 * stateless because they can generate a bookmarkable url. another advantage of a
		 * bookmarkable url is that multiple iframes will not block.
		 */
		return false;
	}
}
