/*        $Author: chenlingfeng7543
//        $Date: 2018/7/26 15:02
//        $Revision: 1.0
*/
package leaf.plugin.export;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.Level;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.fastjson.JSON;
import com.hand.hap.core.impl.RequestHelper;
import com.hand.hls.excel.dto.ExcelExportInfo;
import com.hand.hls.excel.service.IExcelExportInfoService;
import com.hand.hls.utils.SpringContextHolder;
import leaf.application.config.BaseServiceConfig;
import leaf.database.actions.ModelQuery;
import leaf.database.actions.config.ModelQueryConfig;
import leaf.database.service.BusinessModelService;
import leaf.database.service.DatabaseServiceFactory;
import leaf.i18n.ILocalizedMessageProvider;
import leaf.i18n.IMessageProvider;
import leaf.plugin.export.task.IReportTask;
import leaf.plugin.poi.ExcelExportImpl;
import leaf.presentation.component.std.config.DataSetConfig;
import leaf.service.ServiceContext;
import leaf.service.ServiceInstance;
import leaf.service.ServiceOutputConfig;
import leaf.service.http.HttpServiceInstance;
import leaf.utils.ObjectRegistryHolder;
import org.apache.commons.lang3.StringUtils;
import uncertain.composite.CompositeMap;
import uncertain.composite.TextParser;
import uncertain.event.EventModel;
import uncertain.exception.BuiltinExceptionFactory;
import uncertain.logging.ILogger;
import uncertain.logging.LoggingContext;
import uncertain.ocm.IObjectRegistry;

public class ModelOutput {
	public final static String KEY_COLUMN_CONFIG = "_column_config_";
	public final static String KEY_FILE_NAME = "_file_name_";
	public final static String KEY_CHARSET = "GBK";
	public final static String KEY_PROMPT = "prompt";
	public final static String KEY_DATA_INDEX = "name";
	public final static String KEY_MERGE_COLUMN = "_merge_column_";
	public final static String KEY_COLUMN = "column";
	public final static String KEY_WIDTH = "width";
	public final static String KEY_GENERATE_STATE = "_generate_state";
	public final static String KEY_FORMAT = "_format";
	public final static String KEY_SEPARATOR = "separator";
	
	public final static String KEY_ENABLETASK = "enableTask";
	private File excelDir;
	
	public final static String KEY_XLSX = "xlsx";
	public final static String KEY_TXT = "txt";
	public final static String KEY_XLS = "xls";
	public final static String KEY_XLS_MEMORY = "xls_memory";


	IObjectRegistry mObjectRegistry;

	int modelQueryTagNum;

	public ModelOutput(IObjectRegistry registry) {
		mObjectRegistry = registry;
	}

	public int preInvokeService(ServiceContext context) throws Exception {
		CompositeMap parameters = context.getParameter();
		if (!parameters.getBoolean(KEY_GENERATE_STATE, false))
			return EventModel.HANDLE_NORMAL;
//		Excel导出
		boolean isAsync = true;// "Y".equals(parameters.getString("__async__");
		//if(isAsync) {
		if("Y".equals(parameters.getString("__async__"))){
			DatabaseServiceFactory dsf = ObjectRegistryHolder.getInstanceOfType(DatabaseServiceFactory.class);
			String bmName = context.getString("requested_bm_name");
			if(bmName != null) {
				BusinessModelService modelService = dsf.getModelService(bmName, context.getObjectContext());
				StringBuffer querySql = modelService.getSql("query");
				int index = querySql.indexOf(":@");
				int lastIndex = 0;
				while (index > 0) {
//                可能存在${:@trx_id}
//                右边大括号
					int right = querySql.indexOf("}", index);
					int left = -1;
					int pos = index - 1;
					while (left < 0 && pos > lastIndex) {
						char c = querySql.charAt(pos);
						if (c == '$') {
							left = pos;
						} else {
							pos--;
						}
					}
					String fullString = querySql.substring(left, right+1);
					String valuePath = StringUtils.replaceChars(fullString, "${:}", "");
					Object object = parameters.getObject(valuePath);
					if (object == null) {
						throw new RuntimeException("Can not get value from: " + valuePath);
					}
					querySql.replace(left, right+1, object.toString());

					lastIndex = index;
					index = querySql.lastIndexOf(":@");
				}
				List<CompositeMap> columns = context.getParameter().getChild("_column_config_").getChild("column").getChilds();
				transfer(columns);

				String fileName = parameters.getString(KEY_FILE_NAME, "excel");
				String format = parameters.getString(KEY_FORMAT);

				ExcelExportInfo info = new ExcelExportInfo();
				info.setColumnInfo(JSON.toJSONString(columns));
				info.setSqlContent(TextParser.parse(querySql.toString().replaceAll("\\$\\{.*?}", "'$0'"), parameters));
				info.setStatus("NEW");
				info.setFileName(fileName);
				info.setFileType(format);
				IExcelExportInfoService service = SpringContextHolder.getBean(IExcelExportInfoService.class);
				service.insertSelective(RequestHelper.getCurrentRequest(), info);
				return EventModel.HANDLE_NORMAL;
			} else {
				parameters.put("__excel_export__", "Y");
			}
		}

//		End Excel导出
		ILogger mLogger = LoggingContext.getLogger("leaf.plugin.export",
				mObjectRegistry);
		ServiceInstance svc = ServiceInstance.getInstance(context.getObjectContext());
		// 修改fetchall为ture
		CompositeMap config = svc.getServiceConfigData().getChild(
				BaseServiceConfig.KEY_INIT_PROCEDURE);
		if (config == null) {
			mLogger.log(Level.SEVERE, "init-procedure tag must be defined");
			throw new ServletException("init-procedure tag must be defined");
		}

		String return_path = (String) svc.getServiceConfigData().getObject(
				ServiceOutputConfig.KEY_SERVICE_OUTPUT + "/@"
						+ ServiceOutputConfig.KEY_OUTPUT);
		if (return_path == null) {
			mLogger.log(Level.SEVERE, "service-output tag must be defined");
			throw new ServletException("service-output tag must be defined");
		} else {
			if (!return_path.startsWith("/"))
				return_path = "/" + return_path;
		}
		modelQueryTagNum = 0;
		if (createConsumerTag(config, return_path, parameters)) {
			if (modelQueryTagNum == 0) {
				mLogger.log(Level.SEVERE, "The path '" + return_path
						+ "' can't find model-query tag");
				throw new ServletException("The path '" + return_path
						+ "' can't find model-query tag");
			}
		}
		boolean enableTask = isEnableTask(parameters);
		if (enableTask){
			if (excelDir == null) {
				IReportTask excelTask = (IReportTask) mObjectRegistry.getInstanceOfType(IReportTask.class);
				if (excelTask == null)
					throw BuiltinExceptionFactory.createInstanceNotFoundException(null, IReportTask.class, this.getClass().getCanonicalName());
				File excelDirectory = new File(excelTask.getReportDir());
				if (!excelDirectory.exists())
					throw new IllegalArgumentException("File " + excelTask.getReportDir() + " is not exits!");
				if (!excelDirectory.isDirectory())
					throw new IllegalArgumentException("File " + excelTask.getReportDir() + " is not directory!");
				excelDir = excelDirectory;
			}
		}
		return EventModel.HANDLE_NORMAL;
	}

	private void transfer(List<CompositeMap> list){
		if(list == null || list.size() < 1){
			return;
		}
		for (CompositeMap map : list) {
			Object column = map.getObject("column");
			if(column != null){
				CompositeMap compositeMap = (CompositeMap) column;
				List<CompositeMap> childs = compositeMap.getChilds();
				transfer(childs);
				map.put("column", childs);
			}
		}
	}
	boolean createConsumerTag(CompositeMap actionConfig, String return_path,
			CompositeMap parameters) {
		if (modelQueryTagNum != 0)
			return true;
		Iterator iterator = actionConfig.getChildIterator();
		if (iterator != null) {
			while (iterator.hasNext()) {
				if (createConsumerTag((CompositeMap) iterator.next(),
						return_path, parameters)) {
					break;
				}
			}
		} else {
			String rootpath = actionConfig.getString("rootpath");
			if (rootpath != null) {
				if (!rootpath.startsWith("/"))
					rootpath = "/model/" + rootpath;
				if ("model-query".equals(actionConfig.getName())
						&& return_path.equalsIgnoreCase(rootpath)) {
					actionConfig.putBoolean(DataSetConfig.PROPERTITY_FETCHALL,
							true);
					String format = parameters.getString(KEY_FORMAT);
					if (KEY_XLSX.equals(format)||KEY_XLS.equals(format)) {
						actionConfig
								.createChildByTag("consumer")
								.createChildByTag("output-excel")
								.setNameSpaceURI(
										"http://www.leaf-framework.org/application");
						modelQueryTagNum++;
						return true;
					}
					if (KEY_TXT.equals(format)) {
						actionConfig
								.createChildByTag("consumer")
								.createChildByTag("output-txt")
								.setNameSpaceURI(
										"http://www.leaf-framework.org/application");
						modelQueryTagNum++;
						return true;
					}					
				}
			}
		}
		return false;
	}

	public int preCreateSuccessResponse(ServiceContext context)
			throws Exception {
		CompositeMap parameter = context.getParameter();
		if (!parameter.getBoolean(KEY_GENERATE_STATE, false))
			return EventModel.HANDLE_NORMAL;
		if (!KEY_XLS_MEMORY.equals(parameter.getString(KEY_FORMAT)))
			return EventModel.HANDLE_STOP;
		// get ILocalizedMessageProvider
		IMessageProvider msgProvider = (IMessageProvider) mObjectRegistry
				.getInstanceOfType(IMessageProvider.class);
		String langString = context.getSession().getString("lang", "ZHS");
		ILocalizedMessageProvider localMsgProvider = msgProvider
				.getLocalizedMessageProvider(langString);

		ServiceInstance svc = ServiceInstance.getInstance(context
				.getObjectContext());
		ExcelExportImpl excelFactory = new ExcelExportImpl(localMsgProvider);
		if (!isEnableTask(parameter)) {
			HttpServletResponse response = ((HttpServiceInstance) svc).getResponse();

			String fileName = parameter.getString(KEY_FILE_NAME, "excel");
			response.setContentType("application/vnd.ms-excel");
			response.setCharacterEncoding(KEY_CHARSET);
			String userAgent = ((HttpServiceInstance) svc).getRequest().getHeader("User-Agent");
			if (userAgent != null) {
				userAgent = userAgent.toLowerCase();
				if (userAgent.indexOf("msie") != -1) {
					fileName=new String(fileName.getBytes("GBK"),"ISO-8859-1");
				}else{
					fileName=new String(fileName.getBytes("UTF-8"),"ISO-8859-1");
				}
			}
			response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + ".xls\"");
			response.setHeader("cache-control", "must-revalidate");
			response.setHeader("pragma", "public");	
			excelFactory.createExcel(getExportData(context), getColumnConfig(context), response.getOutputStream(), (CompositeMap) context
					.getParameter().getChild(this.KEY_MERGE_COLUMN));
		} else {
			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
			String date = dateFormat.format(new Date());
			String fileName = parameter.getString(KEY_FILE_NAME, "excel_") + date + "_" + System.currentTimeMillis() + ".xls";
			File excel = new File(excelDir, fileName);
			if (excel.createNewFile()) {
				OutputStream os = new FileOutputStream(excel);
				try {
					excelFactory.createExcel(getExportData(context), getColumnConfig(context), os, (CompositeMap) context
							.getParameter().getChild(this.KEY_MERGE_COLUMN));
				} finally {
					if (os != null) {
						os.flush();
						os.close();
					}
				}
			}
			parameter.put("file_path", excel.getCanonicalPath());
		}

		return EventModel.HANDLE_STOP;
	}

	CompositeMap getExportData(ServiceContext context) throws ServletException {
		ILogger mLogger = LoggingContext.getLogger("leaf.plugin.export",
				mObjectRegistry);
		ServiceInstance svc = ServiceInstance.getInstance(context
				.getObjectContext());
		String return_path = (String) svc.getServiceConfigData().getObject(
				ServiceOutputConfig.KEY_SERVICE_OUTPUT + "/@"
						+ ServiceOutputConfig.KEY_OUTPUT);
		if (return_path == null) {
			mLogger.log(Level.SEVERE, "service-output must be defined");
			throw new ServletException("service-output must be defined");
		}
		CompositeMap exportData = (CompositeMap) context.getObjectContext()
				.getObject(return_path);
		return exportData;
	}

	CompositeMap getColumnConfig(ServiceContext context)
			throws ServletException {
		ILogger mLogger = LoggingContext.getLogger("leaf.plugin.export",
				mObjectRegistry);
		CompositeMap column_config = (CompositeMap) context.getParameter()
				.getObject(KEY_COLUMN_CONFIG + "/" + KEY_COLUMN);
		if (column_config == null) {
			mLogger.log(Level.SEVERE,
					"_column_config_ tag and column attibute must be defined");
			throw new ServletException(
					"_column_config_ tag and column attibute must be defined");
		}
		CompositeMap contextMap = context.getObjectContext();
		CompositeMap datatype = (CompositeMap) contextMap
				.getObject("/_export_datatype");
		if (datatype != null) {
			Iterator it = datatype.getChildIterator();
			if (it != null) {
				while (it.hasNext()) {
					CompositeMap record = (CompositeMap) it.next();
					String name = record.getString("field");
					CompositeMap columnRecord = column_config.getChildByAttrib(
							"record", "name", name);
					columnRecord.put(ExcelExportImpl.KEY_DATA_TYPE, record
							.getString(ExcelExportImpl.KEY_DATA_TYPE
									.toLowerCase()));
				}
			}
		}
		return column_config;
	}
	private boolean isEnableTask(CompositeMap parameter){
		if(parameter == null)
			return false;
		boolean enableTask = parameter.getBoolean(KEY_ENABLETASK, false);
		return enableTask;
	}
}