/**
 * Licensed under the Artistic License; you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://displaytag.sourceforge.net/license.html
 *
 * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */
package org.displaytag.pagination;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.displaytag.util.Href;
import org.displaytag.util.HtmlAttributeMap;


/**
 * Helper class for generation of paging banners.
 * @author Fabrizio Giustina
 * @version $Revision: 1.3 $ ($Author: maarten.van.looij $)
 */
public class Pagination
{

	/**
	 * logger.
	 */
	private static Log log = LogFactory.getLog(Pagination.class);

	/**
	 * Base href for urls.
	 */
	private Href href;

	/**
	 * page parameter name.
	 */
	private String pageParam;

	/**
	 * first page.
	 */
	private Integer firstPage;

	/**
	 * last page.
	 */
	private Integer lastPage;

	/**
	 * previous page.
	 */
	private Integer previousPage;

	/**
	 * next page.
	 */
	private Integer nextPage;

	/**
	 * current page.
	 */
	private Integer currentPage;

	/**
	 * List containg NumberedPage objects.
	 * 
	 * @see org.displaytag.pagination.NumberedPage
	 */
	private List pages = new ArrayList();

	/**
	 * Constructor for Pagination.
	 * 
	 * @param baseHref
	 *            Href used for links
	 * @param pageParameter
	 *            name for the page parameter
	 */
	public Pagination(Href baseHref, String pageParameter) {
		this.href = baseHref;
		this.pageParam = pageParameter;
	}

	/**
	 * Adds a page.
	 * 
	 * @param number
	 *            int page number
	 * @param isSelected
	 *            is the page selected?
	 */
	public void addPage(int number, boolean isSelected) {
		if (log.isDebugEnabled()) {
			log.debug("adding page " + number);
		}
		this.pages.add(new NumberedPage(number, isSelected));
	}

	/**
	 * first page selected?
	 * 
	 * @return boolean
	 */
	public boolean isFirst() {
		return this.firstPage == null;
	}

	/**
	 * last page selected?
	 * 
	 * @return boolean
	 */
	public boolean isLast() {
		return this.lastPage == null;
	}

	/**
	 * only one page?
	 * 
	 * @return boolean
	 */
	public boolean isOnePage() {
		return (this.pages == null) || this.pages.size() <= 1;
	}

	/**
	 * Gets the number of the first page.
	 * 
	 * @return Integer number of the first page
	 */
	public Integer getFirst() {
		return this.firstPage;
	}

	/**
	 * Sets the number of the first page.
	 * 
	 * @param first
	 *            Integer number of the first page
	 */
	public void setFirst(Integer first) {
		this.firstPage = first;
	}

	/**
	 * Gets the number of the last page.
	 * 
	 * @return Integer number of the last page
	 */
	public Integer getLast() {
		return this.lastPage;
	}

	/**
	 * Sets the number of the last page.
	 * 
	 * @param last
	 *            Integer number of the last page
	 */
	public void setLast(Integer last) {
		this.lastPage = last;
	}

	/**
	 * Gets the number of the previous page.
	 * 
	 * @return Integer number of the previous page
	 */
	public Integer getPrevious() {
		return this.previousPage;
	}

	/**
	 * Sets the number of the previous page.
	 * 
	 * @param previous
	 *            Integer number of the previous page
	 */
	public void setPrevious(Integer previous) {
		this.previousPage = previous;
	}

	/**
	 * Gets the number of the next page.
	 * 
	 * @return Integer number of the next page
	 */
	public Integer getNext() {
		return this.nextPage;
	}

	/**
	 * Sets the number of the next page.
	 * 
	 * @param next
	 *            Integer number of the next page
	 */
	public void setNext(Integer next) {
		this.nextPage = next;
	}

	/**
	 * Sets the number of the current page.
	 * 
	 * @param current
	 *            number of the current page
	 */
	public void setCurrent(Integer current) {
		this.currentPage = current;
	}

	/**
	 * Returns the appropriate banner for the pagination.
	 * 
	 * @param numberedPageFormat
	 *            String to be used for a not selected page
	 * @param numberedPageSelectedFormat
	 *            String to be used for a selected page
	 * @param numberedPageSeparator
	 *            separator beetween pages
	 * @param fullBanner
	 *            String basic banner
	 * @return String formatted banner whith pages
	 */
	public String getFormattedBanner(String numberedPageFormat,
			String numberedPageSelectedFormat, String numberedPageSeparator,
			String fullBanner) {
		StringBuffer buffer = new StringBuffer(100);

		StringBuffer formPost = new StringBuffer(200); // Added by g.agnelli 19-set-2006 

		// numbered page list
		Iterator pageIterator = this.pages.iterator();

		while (pageIterator.hasNext()) {

			// get NumberedPage from iterator
			NumberedPage page = (NumberedPage) pageIterator.next();

			Integer pageNumber = new Integer(page.getNumber());

			String urlString = ((Href) this.href.clone()).addParameter(
					this.pageParam, pageNumber).toString();

			// Added by g.agnelli 19-set-2006 - Start
			if ((((Href) this.href.clone()).addParameter(this.pageParam,
					pageNumber)).toString().length() > 2000) {
				Href postHref = ((Href) this.href.clone()).addParameter(
						this.pageParam, pageNumber);
				Object mapKey[] = postHref.getParameterMap().keySet().toArray();

				String uid = "GoToPage" + pageNumber;

				formPost.append("<form name=\"form" + uid
						+ "\"  method=\"post\" action=\""
						+ postHref.getBaseUrl() + "\">");
				for (int idx = 0; idx < mapKey.length; idx++) {
					String value = "";
					// Modified by Indicia 07-mar-2007 - Begin
					if (postHref.getParameterMap().get(mapKey[idx]) instanceof Object[]) {
						Object[] values = (Object[])postHref.getParameterMap().get(mapKey[idx]);
						for(int j=0; j < values.length; j++) {
							value = ((Object)values[j]).toString();
							try {
								formPost.append("<input type=\"hidden\" name=\""
										+ mapKey[idx] + "\" value=\"" + URLDecoder.decode(value, "ISO-8859-1") + "\">");
							} catch (UnsupportedEncodingException ignore) {}
						}						
					} else {
						value = postHref.getParameterMap().get(mapKey[idx])
								.toString();
						try {
							formPost.append("<input type=\"hidden\" name=\""
									+ mapKey[idx] + "\" value=\"" + URLDecoder.decode(value, "ISO-8859-1") + "\">");
						} catch (UnsupportedEncodingException ignore) {}
					}
					// Modified by Indicia 07-mar-2007 - End
				}
				formPost.append("</form>");
				Href postExportHerf = (Href) postHref.clone();
				// Modified by Indicia 07-mar-2007 - Begin
				postExportHerf.setFullUrl("javascript:document.forms['form" + uid
						+ "'].submit();");
				// Modified by Indicia 07-mar-2007 - End
				postExportHerf.setParameterMap(new HtmlAttributeMap());
				urlString = postExportHerf.toString();
			}
			// Added by g.agnelli 19-set-2006 - End

			// needed for MessageFormat : page number/url
			Object[] pageObjects = { pageNumber, urlString };

			// selected page need a different formatter
			if (page.getSelected()) {
				buffer.append(MessageFormat.format(numberedPageSelectedFormat,
						pageObjects));
			} else {
				buffer.append(MessageFormat.format(numberedPageFormat,
						pageObjects));
			}

			// next? add page separator
			if (pageIterator.hasNext()) {
				buffer.append(numberedPageSeparator);
			}
		}

		// String for numbered pages
		String numberedPageString = buffer.toString();
		// Object array
		// {0} full String for numbered pages
		// {1} first page url
		// {2} previous page url
		// {3} next page url
		// {4} last page url
		// {5} current page
		// {6} total pages
		Object[] pageObjects = {
				numberedPageString,
				((Href) this.href.clone()).addParameter(this.pageParam,
						getFirst()),
				((Href) this.href.clone()).addParameter(this.pageParam,
						getPrevious()),
				((Href) this.href.clone()).addParameter(this.pageParam,
						getNext()),
				((Href) this.href.clone()).addParameter(this.pageParam,
						getLast()), this.currentPage,
				this.isLast() ? this.currentPage : this.lastPage }; // this.lastPage
		// is null
		// if the
		// last page
		// is
		// displayed

		// return the full banner
		fullBanner += formPost.toString();// Added by g.agnelli 19-set-2006
		
		return MessageFormat.format(fullBanner, pageObjects);
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) //
				.append("firstPage", this.firstPage) //$NON-NLS-1$
				.append("lastPage", this.lastPage) //$NON-NLS-1$
				.append("currentPage", this.currentPage) //$NON-NLS-1$
				.append("nextPage", this.nextPage) //$NON-NLS-1$
				.append("previousPage", this.previousPage) //$NON-NLS-1$
				.append("pages", this.pages) //$NON-NLS-1$
				.append("href", this.href) //$NON-NLS-1$
				.append("pageParam", this.pageParam) //$NON-NLS-1$
				.toString();
	}
}