/*
 * This file is part of Nuts Framework.
 * Copyright (C) 2009 Nuts Develop Team.
 *
 * Nuts Framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License any later version.
 * 
 * Nuts Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Nuts Framework. If not, see <http://www.gnu.org/licenses/>.
 */
package nuts.exts.struts2.actions;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import nuts.core.lang.ExceptionUtils;
import nuts.core.lang.StringEscapeUtils;
import nuts.core.lang.StringUtils;
import nuts.core.net.SendMail;
import nuts.core.servlet.Browser;
import nuts.core.servlet.HttpServletUtils;
import nuts.core.servlet.RequestLoggingFilter;
import nuts.core.util.Pager;
import nuts.core.util.Sorter;
import nuts.exts.app.AppConstants;
import nuts.exts.app.WebApplication;
import nuts.exts.app.WebPlatform;
import nuts.exts.struts2.util.StrutsContextUtils;
import nuts.exts.struts2.views.freemarker.FreemarkerUtils;
import nuts.exts.xwork2.StateProvider;
import nuts.exts.xwork2.util.ContextUtils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.mail.Email;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;

import com.opensymphony.xwork2.util.ValueStack;

import freemarker.template.TemplateException;



/**
 */
public class ActionUtilities extends ActionHelper implements AppConstants {

	/**
	 * Constructor
	 * @param action action
	 */
	public ActionUtilities(CommonAction action) {
		super(action);
	}
	/**
	 * @return app version
	 */
	public String getAppVersion() {
		return WebApplication.getAppVersion();
	}
	
	/**
	 * @param key key
	 * @return the property
	 */
	public String getAppProperty(String key) {
		return WebApplication.getProperty(key);
	}
	
	/**
	 * @param key key
	 * @param def default value
	 * @return the property
	 */
	public String getAppProperty(String key, String def) {
		return WebApplication.getProperty(key, def);
	}
	
	/**
	 * @return true if database resource loader is activated
	 */
	public boolean isDatabaseResourceLoader() {
		return WebApplication.getDatabaseResourceLoader() != null;
	}
	
	/**
	 * @return log
	 */
	public Log getLog() {
		return LogFactory.getLog(action.getClass());
	}
	
	/**
	 * @return true if action log is debug enabled
	 */
	public boolean isDebugEnabled() {
		return getLog().isDebugEnabled();
	}

	/**
	 * @return servlet exception stack trace
	 */
	public String getServletExceptionStackTrace() {
		HttpServletRequest req = StrutsContextUtils.getServletRequest();
		Throwable ex = (Throwable)req.getAttribute("javax.servlet.error.exception");
		if (ex != null) {
			return ExceptionUtils.getFullStackTrace(ex);
		}
		else {
			return "";
		}
	}

	/**
	 * @return relativeURI
	 */
	public String getRelativeURI() {
		return HttpServletUtils.getRelativeURI(StrutsContextUtils.getServletRequest());
	}

	/**
	 * @return requestURI
	 */
	public String getRequestURI() {
		return HttpServletUtils.getRequestURI(StrutsContextUtils.getServletRequest());
	}
	
	/**
	 * @return requestURL
	 */
	public String getRequestURL() {
		return getRequestURL(0);
	}
	
	/**
	 * @param maxLength max length of url
	 * @return requestURL
	 */
	public String getRequestURL(int maxLength) {
		HttpServletRequest req = StrutsContextUtils.getServletRequest();
		String url = (String)req.getAttribute(RequestLoggingFilter.REQUEST_URL);
		if (StringUtils.isEmpty(url)) {
			url = (String)req.getAttribute("javax.servlet.forward.request_uri");
			if (StringUtils.isEmpty(url)) {
				url = req.getRequestURI();
			}
		}
		if (maxLength > 0 && url.length() > maxLength) {
			url = StringUtils.ellipsis(url, 100);
		}
		return url;
	}
	
	/**
	 * @return requestElapsedTime
	 */
	public String getRequestElapsedTime() {
		HttpServletRequest req = StrutsContextUtils.getServletRequest();
		Long start = (Long)req.getAttribute(RequestLoggingFilter.REQUEST_TIME);
		if (start != null) {
			long end = System.currentTimeMillis();
			long elapse = end - start;
			if (elapse < 1000) {
				return elapse + "ms";
			}
			else {
				return (elapse / 1000) + "s";
			}
		}
		return "";
	}
	
	/**
	 * @return csv file time string
	 */
	public String getCsvFileTime() {
		String format = getAction().getText("date-format-file", "_(yyyyMMdd-HHmmss)");
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		return sdf.format(Calendar.getInstance().getTime());
	}

	/**
	 * @return system date
	 */
	public Date getSystemDate() {
		return Calendar.getInstance().getTime();
	}

	/**
	 * @return system year
	 */
	public String getSystemYear() {
		return String.valueOf(Calendar.getInstance().get(Calendar.YEAR));
	}

	/**
	 * @return browser string
	 */
	public String getBrowser() {
		HttpServletRequest hsr = StrutsContextUtils.getServletRequest();
		Browser b = new Browser(hsr);
		return b.toSimpleString();
	}
	
	/**
	 * load sorter parameters from stateProvider
	 * @param sorter sorter
	 * @throws Exception if an error occurs
	 */
	public void loadSorterParams(Sorter sorter) throws Exception {
		StateProvider sp = getAction().getStateProvider();
		
		if (StringUtils.isEmpty(sorter.getColumn())) {
			String sc = (String)sp.loadState("list.sc");
			if (StringUtils.isEmpty(sc)) {
				String tx = ContextUtils.getActionMethod() + NutsTextConstants.SORTER_COLUMN_SUFFIX;
				sc = getAction().getText(tx, (String)null);
				if (sc == null && !NutsTextConstants.LIST_SORTER_COLUMN.equals(tx)) {
					sc = getAction().getText(NutsTextConstants.LIST_SORTER_COLUMN, (String)null);
				}
			}
			if (StringUtils.isNotEmpty(sc)) {
				sorter.setColumn(sc);
			}
		}
		if (StringUtils.isNotEmpty(sorter.getColumn())) {
			if (StringUtils.isEmpty(sorter.getDirection())) {
				String sd = (String)sp.loadState("list.sd");
				if (StringUtils.isEmpty(sd)) {
					String tx = ContextUtils.getActionMethod() + NutsTextConstants.SORTER_DIRECTION_SUFFIX;
					sd = getAction().getText(tx, (String)null);
					if (sd == null && !NutsTextConstants.LIST_SORTER_DIRECTION.equals(tx)) {
						sd = getAction().getText(NutsTextConstants.LIST_SORTER_DIRECTION, (String)null);
					}
					if (sd == null) {
						sd = Sorter.ASC;
					}
				}
				sorter.setDirection(sd);
			}
		}
	}
	
	/**
	 * load pager limit parameters from stateProvider
	 * @param pager pager
	 * @throws Exception if an error occurs
	 */
	public void loadLimitParams(Pager pager) throws Exception {
		StateProvider sp = getAction().getStateProvider();
		if (pager.getLimit() == null || pager.getLimit() < 1) {
			try {
				pager.setLimit(Integer.parseInt((String)sp.loadState("list.pl")));
			}
			catch (Exception e) {
			}
		}
		if (pager.getLimit() == null || pager.getLimit() < 1) {
			String tx = ContextUtils.getActionMethod() + NutsTextConstants.PAGER_LIMIT_SUFFIX;
			Integer l = getAction().getTextAsInt(tx);
			if (l == null && !NutsTextConstants.LIST_PAGER_LIMIT.equals(tx)) {
				l = getAction().getTextAsInt(NutsTextConstants.LIST_PAGER_LIMIT);
			}
			if (l == null) {
				l = NutsTextConstants.DEFAULT_LIST_PAGER_LIMIT;
			}
			pager.setLimit(l);
		}
		limitPage(pager);
	}
	
	/**
	 * save sorter parameters to stateProvider
	 * @param sorter sorter
	 * @throws Exception if an error occurs
	 */
	public void saveSorterParams(Sorter sorter) throws Exception {
		StateProvider sp = getAction().getStateProvider();
		sp.saveState("list.sc", sorter.getColumn());
		sp.saveState("list.sd", sorter.getDirection());
	}
	
	/**
	 * save pager limit parameters to stateProvider
	 * @param pager pager
	 * @throws Exception if an error occurs
	 */
	public void saveLimitParams(Pager pager) throws Exception {
		StateProvider sp = getAction().getStateProvider();
		sp.saveState("list.pl", pager.getLimit());
	}
	
	/**
	 * if pager.limit > maxLimit then set pager.limit = maxLimit
	 * @param pager pager
	 */
	public void limitPage(Pager pager) {
		int maxLimit = getAction().getTextAsInt(NutsTextConstants.PAGER_MAX_LIMIT, 100);
		if (pager.getLimit() == null 
				|| pager.getLimit() < 1 
				|| pager.getLimit() > maxLimit) {
			pager.setLimit(maxLimit);
		}
	}
	

	/**
	 * @see StringEscapeUtils#escapeHtml(String)
	 */
	public String escapeHtml(String str) {
		return StringEscapeUtils.escapeHtml(str);
	}
	
	/**
	 * @see StringEscapeUtils#escapePhtml(String)
	 */
	public String escapePhtml(String str) {
		return StringEscapeUtils.escapePhtml(str);
	}
	
	/**
	 * @see StringEscapeUtils#escapeJava(String)
	 */
	public String escapeJava(String str) {
		return StringEscapeUtils.escapeJava(str);
	}
	
	/**
	 * @see StringEscapeUtils#escapeJavaScript(String)
	 */
	public String escapeJavaScript(String str) {
		return StringEscapeUtils.escapeJavaScript(str);
	}
	
	/**
	 * @see StringEscapeUtils#escapeXml(String)
	 */
	public String escapeXml(String str) {
		return StringEscapeUtils.escapeXml(str);
	}
	
	/**
	 * @see StringEscapeUtils#escapeCsv(String)
	 */
	public String escapeCsv(String str) {
		return StringEscapeUtils.escapeCsv(str);
	}
	
	/**
	 * @see StringUtils#formatFileSize(Long)
	 */
	public String formatFileSize(long size) {
		return StringUtils.formatFileSize(size);
	}
	
	/**
	 * @see StringUtils#ellipsis(String, int)
	 */
	public String ellipsis(String str, int len) {
		return StringUtils.ellipsis(str, len);
	}
	
	/**
	 * @see StringUtils#ellipsiz(String, int)
	 */
	public String ellipsiz(String str, int len) {
		return StringUtils.ellipsiz(str, len);
	}
	
	/**
	 * @param name cookie name
	 * @return cookie value
	 */
	public String getCookie(String name) {
		Cookie c = HttpServletUtils.getCookie(StrutsContextUtils.getServletRequest(), name);
		return c != null ? c.getValue() : null;
	}

	/**
	 * @param str string
	 * @return array
	 */
	public String[] split(String str) {
		return StringUtils.split(str);
	}

	/**
	 * @return true if gae support
	 */
	public boolean isGaeSupport() {
		return WebPlatform.isGaeSupport();
	}

	/**
	 * @return random id
	 */
	public String getRandomId() {
		return UUID.randomUUID().toString();
	}

	public void addEmailCc(Email email, String cc) throws EmailException {
		for (String s : splitMail(cc)) {
			email.addCc(s);
		}
	}
	
	public void addEmailBcc(Email email, String bcc) throws EmailException {
		for (String s : splitMail(bcc)) {
			email.addBcc(s);
		}
	}
	
	public Collection<String> splitMail(String str) {
		Set<String> ms = new HashSet<String>();
		
		String[] ss = StringUtils.split(str, " \r\n\t,;\u3000");
		if (ss != null) {
			ms.addAll(Arrays.asList(ss));
		}

		return ms; 
	}

	/**
	 * send email
	 * @param email email
	 * @param name template name
	 * @param context context
	 */
	public void sendTemplateMail(Email email, String name,
			Object context) throws TemplateException, IOException, EmailException {
		ValueStack vs = ContextUtils.getValueStack();
		if (context != null) {
			vs.push(context);
		}
		
		try {
			sendTemplateMail(email, name);
		}
		finally {
			if (context != null) {
				vs.pop();
			}
		}
	}

	/**
	 * send email
	 * @param email email
	 * @param name template name
	 */
	public void sendTemplateMail(Email email, String name)
			throws TemplateException, IOException, EmailException {
		
		String subject = "";
		String content = FreemarkerUtils.processTemplate(name);

		int cr = content.indexOf('\n');
		if (cr > 0) {
			subject = StringUtils.strip(content.substring(0, cr));
			content = content.substring(cr + 1);
		}

		email.setSubject(subject);
		if (email instanceof HtmlEmail) {
			((HtmlEmail)email).setHtmlMsg(content);
		}
		else {
			email.setMsg(content);
		}

		sendMail(email);
	}
	
	/**
	 * send email
	 * @param mail mail
	 * @throws EmailException if an email error occurs
	 */
	public void sendMail(Email email) throws EmailException {
		try {
			String debug = getAppProperty(MAIL_DEBUG);
			if ("true".equals(debug)) {
				email.setDebug(true);
			}

			String charset = getAppProperty(MAIL_CHARSET, "");
			if (StringUtils.isNotEmpty(charset)) {
				email.setCharset(charset);
			}

			email.setFrom(getAppProperty(MAIL_FROM_MAIL), getAppProperty(MAIL_FROM_NAME));

			String host = getAppProperty(MAIL_SMTP_HOST, "");
			if (StringUtils.isNotEmpty(host)) {
				email.setHostName(host);
			}
			
			String port = getAppProperty(MAIL_SMTP_PORT);
			if (StringUtils.isNumeric(port)) {
				email.setSmtpPort(Integer.parseInt(port));
			}

			String ssl = getAppProperty(MAIL_SMTP_SSL);
			if ("true".equals(ssl)) {
				email.setSSL(true);
				if (StringUtils.isNumeric(port)) {
					email.setSslSmtpPort(port);
				}
			}
			
			String tls = getAppProperty(MAIL_SMTP_TLS);
			if ("true".equals(tls)) {
				email.setTLS(true);
			}
			
			String username = getAppProperty(MAIL_SMTP_USER, "");
			if (StringUtils.isNotEmpty(username)) {
				email.setAuthentication(username, getAppProperty(MAIL_SMTP_PASSWORD, ""));
			}
			
			String bounce = getAppProperty(MAIL_SMTP_BOUNCE, "");
			if (StringUtils.isNotEmpty(bounce)) {
				email.setBounceAddress(bounce);
			}

			SendMail.send(email);
		}
		catch (EmailException e) {
			log.warn("send mail failed!", e);
			throw e;
		}
	}

	/**
	 * ignore email exception
	 * @return true if mail-exception is set to warn/ignore
	 */
	public boolean ignoreEmailException() {
		String iee = getAppProperty(MAIL_EXCEPTION, "");
		
		if ("warn".equals(iee) || "ignore".equals(iee)) {
			return true;
		}
		
		return false;
	}
}
