// ***************************************************************************************************************************
// * 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.juneau.rest.widget;

import static org.apache.juneau.internal.StringUtils.*;

import java.io.*;

import org.apache.juneau.html.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.serializer.*;

/**
 * A subclass of widgets for rendering menu items with drop-down windows.
 *
 * <ul class='seealso'>
 * 	<li class='link'>{@doc juneau-rest-server.HtmlDocAnnotation.PredefinedWidgets}
 * </ul>
 */
public abstract class MenuItemWidget extends Widget {

	/**
	 * Returns the Javascript needed for the show and hide actions of the menu item.
	 */
	@Override /* Widget */
	public String getScript(RestRequest req, RestResponse res) throws Exception {
		return loadScript("MenuItemWidget.js");
	}

	/**
	 * Optional Javascript to execute immediately before a menu item is shown.
	 *
	 * <p>
	 * For example, the following shows how the method could be used to make an AJAX call back to the REST
	 * interface to populate a SELECT element in the contents of the popup dialog:
	 *
	 * <p class='bcode w800'>
	 * 	<ja>@Override</ja>
	 * 	<jk>public</jk> String getBeforeShowScript(RestRequest req) {
	 * 		<jk>return</jk> <js>""</js>
	 * 			+ <js>"\n	var xhr = new XMLHttpRequest();"</js>
	 * 			+ <js>"\n	xhr.open('GET', '/petstore/pet?s=status=AVAILABLE&amp;v=id,name', true);"</js>
	 * 			+ <js>"\n	xhr.setRequestHeader('Accept', 'application/json');"</js>
	 * 			+ <js>"\n	xhr.onload = function() {"</js>
	 * 			+ <js>"\n       var pets = JSON.parse(xhr.responseText);"</js>
	 * 			+ <js>"\n 		var select = document.getElementById('addPet_names');"</js>
	 * 			+ <js>"\n 		select.innerHTML = '';"</js>
	 * 			+ <js>"\n 		for (var i in pets) {"</js>
	 * 			+ <js>"\n 			var pet = pets[i];"</js>
	 * 			+ <js>"\n 			var opt = document.createElement('option');"</js>
	 * 			+ <js>"\n 			opt.value = pet.id;"</js>
	 * 			+ <js>"\n 			opt.innerHTML = pet.name;"</js>
	 * 			+ <js>"\n 			select.appendChild(opt);"</js>
	 * 			+ <js>"\n 		}"</js>
	 * 			+ <js>"\n	}"</js>
	 * 			+ <js>"\n	xhr.send();"</js>
	 * 		;
	 * 	}
	 * </p>
	 *
	 * <p>
	 * Note that it's often easier (and cleaner) to use the {@link #loadScript(String)} method and read the Javascript from
	 * your classpath:
	 *
	 * <p class='bcode w800'>
	 * 	<ja>@Override</ja>
	 * 	<jk>public</jk> String getBeforeShowScript(RestRequest req) <jk>throws</jk> Exception {
	 * 		<jk>return</jk> loadScript(<js>"AddOrderMenuItem_beforeShow.js"</js>);
	 * 	}
	 * </p>
	 *
	 * @param req The HTTP request object.
	 * @param res The HTTP response object.
	 * @return Javascript code to execute, or <jk>null</jk> if there isn't any.
	 * @throws Exception Error occurred.
	 */
	public String getBeforeShowScript(RestRequest req, RestResponse res) throws Exception {
		return null;
	}

	/**
	 * Optional Javascript to execute immediately after a menu item is shown.
	 *
	 * <p>
	 * Same as {@link #getBeforeShowScript(RestRequest,RestResponse)} except this Javascript gets executed after the popup dialog has become visible.
	 *
	 * @param req The HTTP request object.
	 * @param res The HTTP response object.
	 * @return Javascript code to execute, or <jk>null</jk> if there isn't any.
	 * @throws Exception Error occurred.
	 */
	public String getAfterShowScript(RestRequest req, RestResponse res) throws Exception {
		return null;
	}

	/**
	 * Defines a <js>"menu-item"</js> class that needs to be used on the outer element of the HTML returned by the
	 * {@link #getHtml(RestRequest,RestResponse)} method.
	 */
	@Override /* Widget */
	public String getStyle(RestRequest req, RestResponse res) throws Exception {
		return loadStyle("MenuItemWidget.css");
	}

	@Override /* Widget */
	public String getHtml(RestRequest req, RestResponse res) throws Exception {
		StringBuilder sb = new StringBuilder();

		// Need a unique number to define unique function names.
		Integer id = null;

		String pre = nullIfEmpty(getBeforeShowScript(req, res)), post = nullIfEmpty(getAfterShowScript(req, res));

		sb.append("\n<div class='menu-item'>");
		if (pre != null || post != null) {
			id = getId(req);

			sb.append("\n\t<script>");
			if (pre != null) {
				sb.append("\n\t\tfunction onPreShow" + id + "() {");
				sb.append("\n").append(pre);
				sb.append("\n\t\t}");
			}
			if (post != null) {
				sb.append("\n\t\tfunction onPostShow" + id + "() {");
				sb.append("\n").append(pre);
				sb.append("\n\t\t}");
			}
			sb.append("\n\t</script>");
		}
		String onclick = (pre == null ? "" : "onPreShow"+id+"();") + "menuClick(this);" + (post == null ? "" : "onPostShow"+id+"();");
		sb.append(""
			+ "\n\t<a onclick='"+onclick+"'>"+getLabel(req, res)+"</a>"
			+ "\n<div class='popup-content'>"
		);
		Object o = getContent(req, res);
		if (o instanceof Reader) {
			try (Reader r = (Reader)o; Writer w = new StringBuilderWriter(sb)) {
				IOUtils.pipe(r, w);
			}
		} else if (o instanceof CharSequence) {
			sb.append((CharSequence)o);
		} else {
			SerializerSessionArgs args =
				SerializerSessionArgs
					.create()
					.properties(req.getAttributes())
					.debug(req.isDebug() ? true : null)
					.uriContext(req.getUriContext())
					.useWhitespace(req.isPlainText() ? true : null)
					.resolver(req.getVarResolverSession());
			WriterSerializerSession session = HtmlSerializer.DEFAULT.createSession(args);
			session.indent = 2;
			session.serialize(o, sb);
		}
		sb.append(""
			+ "\n\t</div>"
			+ "\n</div>"
		);
		return sb.toString();
	}

	private Integer getId(RestRequest req) {
		Integer id = (Integer)req.getAttribute("LastMenuItemId");
		if (id == null)
			id = 1;
		else
			id = id + 1;
		req.setAttribute("LastMenuItemId", id);
		return id;
	}

	/**
	 * The label for the menu item as it's rendered in the menu bar.
	 *
	 * @param req The HTTP request object.
	 * @param res The HTTP response object.
	 * @return The menu item label.
	 * @throws Exception Error occurred.
	 */
	public abstract String getLabel(RestRequest req, RestResponse res) throws Exception;

	/**
	 * The content of the popup.
	 *
	 * @param req The HTTP request object.
	 * @param res The HTTP response object.
	 * @return
	 * 	The content of the popup.
	 * 	<br>Can be any of the following types:
	 * 	<ul>
	 * 		<li>{@link Reader} - Serialized directly to the output.
	 * 		<li>{@link CharSequence} - Serialized directly to the output.
	 * 		<li>Other - Serialized as HTML using {@link HtmlSerializer#DEFAULT}.
	 * 			<br>Note that this includes any of the {@link org.apache.juneau.dto.html5} beans.
	 * 	</ul>
	 * @throws Exception Error occurred.
	 */
	public abstract Object getContent(RestRequest req, RestResponse res) throws Exception;
}
