/*
 * This file is part of Nuts Framework.
 * Copyright(C) 2009-2012 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.dispatcher;

import java.io.PrintWriter;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;
import net.sf.json.processors.DefaultValueProcessor;
import net.sf.json.util.CycleDetectionStrategy;

import nuts.core.json.filters.WildcardPropertyFilter;
import nuts.core.json.processors.FilterValueProcessor;
import nuts.core.json.processors.NullDefaultValueProcessor;
import nuts.core.json.processors.PagerValueProcessor;
import nuts.core.json.processors.QueryValueProcessor;
import nuts.core.json.processors.SorterValueProcessor;
import nuts.core.lang.StringUtils;
import nuts.core.util.Filter;
import nuts.core.util.Pager;
import nuts.core.util.Query;
import nuts.core.util.Sorter;
import nuts.exts.fileupload.UploadFile;
import nuts.exts.fileupload.UploadImage;
import nuts.exts.json.processors.DateValueExProcessor;
import nuts.exts.json.processors.UploadFileValueProcessor;
import nuts.exts.struts2.NutsStrutsConstants;
import nuts.exts.xwork2.converter.converters.DateTypeConverter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;


/**
 * <!-- START SNIPPET: description -->
 *
 * A result that send the content out as plain json text.
 *
 * <!-- END SNIPPET: description -->
 *
 *
 * <!-- START SNIPPET: params -->
 *
 * <ul>
 *  <li>circleDetect (optional) = circleDetect to be used. [strict, lenient, noprop] (default: lenient)</li>
 *  <li>dateFormat (optional) = date format to be used. (default: long - Date.getTime())</li>
 *  <li>filterTypes (optional) = filter specified property types when serialize. (default: java.io.*, java.awt.*, [B, [Ljava.lang.Byte;)</li>
 *  <li>noCache (optional) = no cache to set. (default: true)</li>
 *  <li>contentType (optional) = content type to be used. (default: text/javascript)</li>
 *  <li>encoding (optional) = character encoding to be used. This character encoding will be used to set. (default: UTF-8)</li>
 *  <li>properties (default) = extra properties to be output. { "user": ... }</li>
 * </ul>
 *
 * <!-- END SNIPPET: params -->
 *
 *
 * <pre>
 * <!-- START SNIPPET: example -->
 *
 * &lt;action name="displayJsonContent" &gt;
 *   &lt;result type="json"&gt;username, password, other&lt;/result&gt;
 * &lt;/action&gt;
 *
 * &lt;action name="displayJsonContent" &gt;
 *   &lt;result type="json"&gt;
 *   	&lt;param name="properties"&gt;username, password, other&lt;/param&gt;
 *   &lt;/result&gt;
 * &lt;/action&gt;
 *
 * <!-- END SNIPPET: example -->
 * </pre>
 *
 */
@SuppressWarnings("serial")
public class JsonResult extends AbstractOMResult {

	protected static final Log log = LogFactory.getLog(JsonResult.class);

	/**
	 * Constructor.
	 */
	public JsonResult() {
		super();
		setContentType("text/plain");
	}

	/**
	 * @param shortName the shortName to set
	 */
    @Inject(value=NutsStrutsConstants.NUTS_JSON_SHORT_NAME, required=false)
	public void setBeanShortName(String shortName) {
		this.shortName = Boolean.parseBoolean(shortName);
	}

	/**
	 * write result
	 * @param invocation actionInvocation
	 * @param result result object
	 * @throws Exception
	 */
	@Override
	protected void writeResult(ActionInvocation invocation, Object result) throws Exception {
//		UserAgent ua = new UserAgent(StrutsContextUtils.getServletRequest(invocation));
//
//		if (ua.isMSIE()) {
//			setContentType("application/json");
//		}

		super.writeResult(invocation, result);
	}

	/**
	 * write result
	 * @param writer response writer
	 * @param result result object
	 * @throws Exception
	 */
	@Override
	protected void writeResult(PrintWriter writer, Object result) throws Exception {
		if (result != null) {
			JSONObject json = resolveJson(result);
			if (log.isDebugEnabled()) {
				log.debug(json.toString());
			}
			json.write(writer);
		}
	}

	protected JSONObject resolveJson(Object result) {
		JsonResultConfig jrc = new JsonResultConfig();

		if (cycleDetect != null) {
			if (CYCLE_DETECT_STRICT.equals(cycleDetect)) {
				jrc.setCycleDetectionStrategy(CycleDetectionStrategy.STRICT);
			}
			else if (CYCLE_DETECT_LENIENT.equals(cycleDetect)) {
				jrc.setCycleDetectionStrategy(CycleDetectionStrategy.LENIENT);
			}
			else if (CYCLE_DETECT_NOPROP.equals(cycleDetect)) {
				jrc.setCycleDetectionStrategy(CycleDetectionStrategy.NOPROP);
			}
		}

		jrc.setIgnoreJPATransient(ignoreTransient);
		jrc.setIgnoreTransientFields(ignoreTransient);
		
		DateValueExProcessor dvp = new DateValueExProcessor();
		if (StringUtils.isBlank(dateFormat)) {
			dvp.setDateTypeConverter(new DateTypeConverter());
		}
		else if (DATE_FORMAT_LONG.compareToIgnoreCase(dateFormat) == 0) {
			dvp.setToTime(true);
		}
		else {
			dvp.setDateFormat(dateFormat);
		}
		jrc.registerJsonValueProcessor(Date.class, dvp);
		jrc.registerJsonValueProcessor(Calendar.class, dvp);

		jrc.registerJsonValueProcessor(Filter.class, new FilterValueProcessor(shortName));
		jrc.registerJsonValueProcessor(Pager.class, new PagerValueProcessor(shortName));
		jrc.registerJsonValueProcessor(Query.class, new QueryValueProcessor(shortName));
		jrc.registerJsonValueProcessor(Sorter.class, new SorterValueProcessor(shortName));
		jrc.registerJsonValueProcessor(UploadFile.class, new UploadFileValueProcessor());
		jrc.registerJsonValueProcessor(UploadImage.class, new UploadFileValueProcessor());

		List<String> nvtl = toList(filterTypes);
		jrc.setIgnoreTypes(nvtl);

		return JSONObject.fromObject(result, jrc);
	}

	private class JsonResultConfig extends JsonConfig {
		private DefaultValueProcessor NULL_DEFAULT_VALUE_PROCESSOR = new NullDefaultValueProcessor();

		public JsonResultConfig() {
			super();
		}

		public void setIgnoreTypes(List<String> types) {
			WildcardPropertyFilter ipf = new WildcardPropertyFilter();

			ipf.setFilterTypes(types);
			
			super.setJsonPropertyFilter(ipf);
		}

		/**
		 * Finds a DefaultValueProcessor registered to the target class.<br>
		 * Returns null if none is registered. <br>
		 * Used when transforming from Java to Json.
		 * 
		 * @param target a class used for searching a DefaultValueProcessor.
		 * @return NULL_DEFAULT_VALUE_PROCESSOR
		 */
		public DefaultValueProcessor findDefaultValueProcessor(Class target) {
			return NULL_DEFAULT_VALUE_PROCESSOR;
		}

	}
}
