/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.help.internal.webapp.servlet;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

import org.eclipse.help.*;
import org.eclipse.help.internal.*;
import org.eclipse.help.internal.toc.*;
import org.eclipse.help.internal.webapp.data.*;
/**
 * URL-like description of help table of contents.
 * <ul>
 * <li>toc/pluginid/tocfile.xml: the toc defined by the specified toc xml</li>
 * <li>toc/: all the toc's</li>
 * <li>toc/?topic=/pluginid/topic.html: a list of toc that contain the
 * specified topic</li>
 * </ul>
 */
public class TocServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	private String locale;

	/**
	 * Called by the server (via the <code>service</code> method) to allow a
	 * servlet to handle a GET request.
	 */
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

		locale = UrlUtil.getLocale(req, resp);
		req.setCharacterEncoding("UTF-8"); //$NON-NLS-1$

		resp.setContentType("application/xml; charset=UTF-8"); //$NON-NLS-1$
		resp.setHeader("Cache-Control", "max-age=10000"); //$NON-NLS-1$ //$NON-NLS-2$

		if ("/".equals(req.getPathInfo())) { //$NON-NLS-1$
			if (req.getParameter("topic") == null) //$NON-NLS-1$
				serializeTocs(resp);
			else
				serializeTocs(
						findTocContainingTopic(req.getParameter("topic")), //$NON-NLS-1$
						resp);
		} else {
			serializeToc(req.getPathInfo(), resp);
		}
	}
	/**
	 * 
	 * Called by the server (via the <code>service</code> method) to allow a
	 * servlet to handle a POST request.
	 * 
	 * Handle the search requests,
	 *  
	 */
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		doGet(req, resp);
	}

	/**
	 * XML representation of TOC
	 */
	private void serializeToc(String tocID, HttpServletResponse resp)
			throws ServletException, IOException {
		IToc toc = HelpPlugin.getTocManager().getToc(tocID, locale);
		serializeToc(toc, resp);
	}
	/**
	 * XML representation of TOC
	 */
	private void serializeToc(IToc toc, HttpServletResponse resp)
			throws ServletException, IOException {
		if (toc == null)
			throw new ServletException();

		TocWriter tocWriter = new TocWriter(resp.getWriter());
		tocWriter.generate(toc, true);
		tocWriter.close();
	}

	/**
	 * XML representation of TOC list
	 */
	private void serializeTocs(HttpServletResponse resp)
			throws ServletException, IOException {
		TocManager tocManager = HelpPlugin.getTocManager();
		IToc[] tocs = tocManager.getTocs(locale);

		TocWriter gen = new TocWriter(resp.getWriter());
		gen.println("<tocs>"); //$NON-NLS-1$
		gen.pad++;
		for (int i = 0; i < tocs.length; i++) {
			gen.printPad();
			gen.generate(tocs[i], false);
		}
		gen.pad--;
		gen.println("</tocs>"); //$NON-NLS-1$
		gen.close();
	}

	private void serializeTocs(IToc toc, HttpServletResponse resp)
			throws ServletException, IOException {
		if (toc == null)
			throw new ServletException();

		TocWriter gen = new TocWriter(resp.getWriter());
		gen.println("<tocs>"); //$NON-NLS-1$
		gen.pad++;
		gen.printPad();
		gen.generate(toc, false);
		gen.pad--;
		gen.println("</tocs>"); //$NON-NLS-1$
		gen.close();
	}

	/**
	 * Finds a TOC that contains specified topic
	 * 
	 * @param topic
	 *            the topic href
	 */
	private IToc findTocContainingTopic(String topic) {
		if (topic == null || topic.equals("")) //$NON-NLS-1$
			return null;

		int index = topic.indexOf("/topic/"); //$NON-NLS-1$
		if (index != -1)
			topic = topic.substring(index + 6);
		index = topic.indexOf('?');
		if (index != -1)
			topic = topic.substring(0, index);

		if (topic == null || topic.equals("")) //$NON-NLS-1$
			return null;

		IToc[] tocs = HelpPlugin.getTocManager().getTocs(locale);
		for (int i = 0; i < tocs.length; i++)
			if (tocs[i].getTopic(topic) != null)
				return tocs[i];

		// nothing found
		return null;
	}

	/**
	 * This generates the XML file for the help navigation.
	 */
	private static class TocWriter extends XMLGenerator {
		/**
		 * @param writer
		 *            java.io.Writer
		 */
		public TocWriter(Writer writer) {
			super(writer);
		}

		/**
		 * Writes out xml data for a toc
		 */
		public void generate(IToc toc, boolean genTopics) {
			// get the topic description
			String topicDescription = ""; //$NON-NLS-1$
			ITopic topic = toc.getTopic(null);
			if (topic != null)
				topicDescription = topic.getHref();

			println("<toc label=\"" //$NON-NLS-1$
					+ xmlEscape(toc.getLabel()) + "\" href=\"" //$NON-NLS-1$
					+ reduceURL(toc.getHref()) + "\" topic=\"" //$NON-NLS-1$
					+ reduceURL(topicDescription) + "\">"); //$NON-NLS-1$
			if (genTopics) {
				ITopic[] topics = toc.getTopics();
				for (int i = 0; i < topics.length; i++) {
					generate(topics[i]);
				}
			}
			println("</toc>"); //$NON-NLS-1$
		}

		/**
		 * Generates part of navigation for a given Topic and it children Topic
		 */
		protected void generate(ITopic topic) {
			pad++;
			printPad();
			String href = topic.getHref();
			print("<topic label=\"" //$NON-NLS-1$
					+ xmlEscape(topic.getLabel()) + "\"" //$NON-NLS-1$
					+ (href != null ? " href=\"" + reduceURL(href) + "\"" : "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			ITopic[] subtopics = topic.getSubtopics();
			if (subtopics.length > 0) {
				println(">"); //$NON-NLS-1$
				for (int i = 0; i < subtopics.length; i++) {
					generate(subtopics[i]);
				}
				printPad();
				println("</topic>"); //$NON-NLS-1$
			} else {
				println(" />"); //$NON-NLS-1$
			}
			pad--;
		}

		/**
		 * Simplifies url path by removing "string/.." from the path
		 * 
		 * @return reduced url String
		 * @param url
		 *            String
		 */
		protected static String reduceURL(String url) {
			if (url == null)
				return url;
			while (true) {
				int index = url.indexOf("/..", 1); //$NON-NLS-1$
				if (index <= 0)
					break;
				//there is no "/.." or nothing before "/.." to simplify
				String part1 = url.substring(0, index);
				String part2 = url.substring(index + "/..".length()); //$NON-NLS-1$
				index = part1.lastIndexOf("/"); //$NON-NLS-1$
				if (index >= 0)
					url = part1.substring(0, index) + part2;
				else
					url = part2;
			}
			return url;
		}
	}
}
