package com.gaowj.business.procdefinitionmodels;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.ManagementService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ResourceEntity;
import org.activiti.engine.impl.repository.DeploymentBuilderImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.NativeModelQuery;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.XPath;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.gaowj.business.ProcdefinitionmodelsBusiness;
import com.gaowj.business.exception.BusinessException;
import com.gaowj.business.procdefinition.procdefinitionDAO;
import com.gaowj.business.util.Page;
import com.gaowj.business.util.SessionUtil;

public class ProcdefinitionmodelsBusinessImpl implements ProcdefinitionmodelsBusiness {

	private procdefinitionmodelsDAO procdefinitionmodelsDAO;

	private procdefinitionDAO procdefinitionDAO;

	private RepositoryService repositoryService;

	private ManagementService managementService;

	public void setProcdefinitionmodelsDAO(procdefinitionmodelsDAO procdefinitionmodelsDAO) {
		this.procdefinitionmodelsDAO = procdefinitionmodelsDAO;
	}

	public void setRepositoryService(RepositoryService repositoryService) {
		this.repositoryService = repositoryService;
	}

	public void setProcdefinitionDAO(procdefinitionDAO procdefinitionDAO) {
		this.procdefinitionDAO = procdefinitionDAO;
	}

	public void setManagementService(ManagementService managementService) {
		this.managementService = managementService;
	}

	@Override
	public Map<String, Object> insert_procde_finition_models(Map<String, Object> entity) throws BusinessException {
		// 查询流程记录(为了获取该流程的流程部署key值)
		Map<String, Object> query = new HashMap<String, Object>();
		query.put("UUID", entity.get("PROCID"));
		String deploymentKey = procdefinitionDAO.list_procde_finition(query).get(0).get("DEPLOYMENT_KEY").toString();
		// 添加model
		Model model = createModel(entity.get("MODEL_NAME").toString(), entity.get("DESCRIPTION").toString(), deploymentKey);
		// 添加model和流程表的匹配
		Map<String, Object> procdefinitionmodels = new HashMap<String, Object>();
		procdefinitionmodels.put("MODEL_ID", model.getId());
		procdefinitionmodels.put("PROC_DEFINITION_ID", entity.get("PROCID"));

		if (procdefinitionmodels.get("UUID") == null) {
			procdefinitionmodels.put("UUID", java.util.UUID.randomUUID().toString());
		}
		if (procdefinitionmodels.get("CREATE_TIME") == null) {
			procdefinitionmodels.put("CREATE_TIME", new Date());
		}
		if (procdefinitionmodels.get("CREATE_ID") == null) {
			procdefinitionmodels.put("CREATE_ID", SessionUtil.getCode());
		}
		if (procdefinitionmodels.get("UPDATE_ID") == null) {
			procdefinitionmodels.put("UPDATE_ID", SessionUtil.getCode());
		}
		if (procdefinitionmodels.get("UPDATE_TIME") == null) {
			procdefinitionmodels.put("UPDATE_TIME", new Date());
		}

		// 动态传值插入
		List<String> infoListKey = new ArrayList<String>();
		List<Object> infoList = new ArrayList<Object>();
		if (procdefinitionmodels.keySet() != null) {
			Set<String> key = procdefinitionmodels.keySet();
			// 将map集合中的key和value 取出来分别放到list集合里
			for (String str : key) {
				infoList.add(procdefinitionmodels.get(str));
				infoListKey.add(str);
			}
			procdefinitionmodels.put("infoListKey", infoListKey);
			procdefinitionmodels.put("infoList", infoList);
		}
		procdefinitionmodelsDAO.insert_procde_finition_models(procdefinitionmodels);
		return entity;
	}

	@Override
	public void update_procde_finition_models(Map<String, Object> entity) throws BusinessException {
		// 动态传值修改
		if (entity.keySet() != null) {
			convertEntity(entity);
			procdefinitionmodelsDAO.update_procde_finition_models(entity);
			entity.remove("info");
		}
	}

	@Override
	public void delete_procde_finition_models(List<String> list) throws BusinessException {
		Map<String, Object> query = new HashMap<String, Object>();
		query.put("listUUID", list);
		List<Map<String, Object>> lists = procdefinitionmodelsDAO.list_procde_finition_models(query);
		for (Map<String, Object> m : lists) {
			repositoryService.deleteModel(m.get("MODEL_ID").toString());
		}
		procdefinitionmodelsDAO.delete_procde_finition_models(list);
	}

	@Override
	public Page<Map<String, Object>> list_procde_finition_models(int pageNo, int pageSize, Map<String, Object> query) throws BusinessException {
		// 计算起始记录
		int pageStart = (pageNo - 1) * pageSize;

		// 获取列表(流程定义id下的全部模块id)
		Map<String, Object> query2 = new HashMap<String, Object>();
		query2.put("PROC_DEFINITION_ID", query.get("PROCID"));
		List<Map<String, Object>> items = procdefinitionmodelsDAO.list_procde_finition_models(query2);
		List<String> listId = new ArrayList<String>();
		for (Map<String, Object> m : items) {
			listId.add(m.get("MODEL_ID").toString());
		}
		// 申明一个基于Model的自定义查询
		NativeModelQuery nativeModelQuery = repositoryService.createNativeModelQuery();
		String sql = " FROM " + managementService.getTableName(Model.class) + " T";
		StringBuffer where = new StringBuffer();
		if (listId.size() > 0) {
			StringBuffer condition = new StringBuffer();
			condition.append("T.ID_ IN (");
			for (int i = 0; i < listId.size(); i++) {
				if (i != 0) {
					condition.append(",");
				}
				condition.append("#{modelId" + i + "}");
				nativeModelQuery.parameter("modelId" + i, listId.get(i));
			}
			condition.append(")");
			addCondition(where, condition.toString());
		} else {
			// 如果listId为空,则不需要查询任何数据
			addCondition(where, "1 = 2");
		}
		
		// 设置查询条件
		if (query.get("MODEL_NAME") != null && !"".equals(query.get("MODEL_NAME"))) {
			addCondition(where, "NAME_ LIKE #{modelName}");
			nativeModelQuery.parameter("modelName", "%" + query.get("MODEL_NAME") + "%");
		}
		sql += where.toString();

		// 获取列表个数
		int count = Integer.valueOf(nativeModelQuery.sql("SELECT count(*) " + sql).count() + "");

		List<Model> listModel = nativeModelQuery.sql("SELECT * " + sql).listPage((pageNo - 1) * pageSize, pageSize);
		List<Map<String, Object>> listModelMap = new ArrayList<Map<String, Object>>();
		for (Model model : listModel) {
			Map<String, Object> m = new HashMap<String, Object>();
			listModelMap.add(m);
			m.put("ID", model.getId());
			m.put("REVISION", model.getVersion());
			m.put("NAME", model.getName());
			m.put("KEY", model.getKey());
			m.put("CATEGORY", model.getCategory());
			m.put("CREATETIME", model.getCreateTime());
			m.put("LASTUPDATETIME", model.getLastUpdateTime());
			m.put("METAINFO", model.getMetaInfo());
			m.put("KEY", model.getKey());

			for (Map<String, Object> item : items) {
				if (model.getId().equals(item.get("MODEL_ID"))) {
					m.put("UUID", item.get("UUID"));
					break;
				}
			}

			List<String> listKey = new ArrayList<String>();
			for (String k : m.keySet()) {
				if (m.get(k) == null) {
					listKey.add(k);
				}
			}
			for (String k : listKey) {
				m.remove(k);
			}
		}
		// 创建分页对象
		Page<Map<String, Object>> page = new Page<Map<String, Object>>();
		page.setStart(pageStart);
		page.setLimit(pageSize);
		page.setCount(count);
		page.setItems(listModelMap);

		return page;
	}

	@Override
	public List<Map<String, Object>> list_procde_finition_models(Map<String, Object> query) throws BusinessException {
		machining(query);
		return procdefinitionmodelsDAO.list_procde_finition_models(query);
	}

	@Override
	public int listCount_procde_finition_models(Map<String, Object> query) throws BusinessException {
		machining(query);
		return procdefinitionmodelsDAO.listCount_procde_finition_models(query);
	}

	@Override
	public void deploy(Map<String, Object> query) throws BusinessException {
		String procId = query.get("PROCID").toString();
		String modelId = query.get("MODELID").toString();

		Model modelData = repositoryService.getModel(modelId);
		ObjectNode modelNode = null;
		try {
			modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
		} catch (JsonProcessingException e) {
			throw new BusinessException(e.getMessage());
		} catch (IOException e) {
			throw new BusinessException(e.getMessage());
		}
		byte[] bpmnBytes = null;

		BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
		bpmnBytes = new BpmnXMLConverter().convertToXML(model, "GBK");

		String processName = modelData.getName() + ".bpmn20.xml";
		
		String bpmText=new String(bpmnBytes);
		try {
			//自动添加任务监听
			Document doc = DocumentHelper.parseText(bpmText);
			Element rootEle = doc.getRootElement();  
			String nsUri = rootEle.getNamespaceURI();  
			Map nsMap = new HashMap();  
			nsMap.put("rootNs", nsUri);  
			XPath mesXpath = doc.createXPath("/rootNs:definitions/rootNs:process/rootNs:userTask");  
			mesXpath.setNamespaceURIs(nsMap);  
			List listNodes=mesXpath.selectNodes(doc);
			if(listNodes.size()>0){
				for(Object node:listNodes){
					Element node_e =(Element) node;
					Element extensionElements=node_e.addElement("extensionElements");
					Element taskListener=extensionElements.addElement("activiti:taskListener");
					taskListener.addAttribute("event", "create");
					taskListener.addAttribute("class", "com.gaowj.activiti.listener.AssignmentTaskListener");
				}
			}
			bpmText=doc.asXML();
		} catch (DocumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		Deployment deployment = repositoryService.createDeployment().name(modelData.getName()).addString(processName, bpmText,"GBK").deploy();
		// 将部署id存入流程定义表
		Map<String, Object> entity = new HashMap<String, Object>();
		entity.put("UUID", procId);
		entity.put("DEPLOYMENT_ID", deployment.getId());
		convertEntity(entity);
		procdefinitionDAO.update_procde_finition(entity);
	}

	/**
	 * 更新或者插入时,处理map对象的形式(将字段信息存储为list,便于mybatis代码调用)
	 * 
	 * @param entity
	 */
	private void convertEntity(Map<String, Object> entity, String... ignoreKeys) {
		List<Map<String, Object>> updateList = new ArrayList<Map<String, Object>>();
		Set<String> key = entity.keySet();
		// 将map集合中的key和value 取出来分别放到list集合里
		for (String str : key) {
			Map<String, Object> updateMap = new HashMap<String, Object>();

			for (String k : ignoreKeys) {
				if (StringUtils.equalsIgnoreCase("UUID", str))
					continue;
			}
			updateMap.put("key", str);
			updateMap.put("value", entity.get(str));

			updateList.add(updateMap);
		}
		entity.put("info", updateList);
	}

	private void machining(Map<String, Object> query) {
		if (query.get("PROC_NAME") != null && !"".equals(query.get("PROC_NAME"))) {
			query.put("PROC_NAME", "%" + query.get("PROC_NAME") + "%");
		}
	}

	/**
	 * 添加model
	 * 
	 * @param modelName
	 * @param description
	 * @param modelKey
	 * @return
	 */
	private Model createModel(String modelName, String description, String deploymentKey) {
		ObjectMapper objectMapper = new ObjectMapper();
		ObjectNode editorNode = objectMapper.createObjectNode();
		editorNode.put("id", "canvas");
		editorNode.put("resourceId", "canvas");
		ObjectNode stencilSetNode = objectMapper.createObjectNode();
		stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
		editorNode.put("stencilset", stencilSetNode);
		ObjectNode properties = objectMapper.createObjectNode();
		properties.put("process_id", deploymentKey);
		editorNode.put("properties", properties);

		Model modelData = repositoryService.newModel();
		ObjectNode modelObjectNode = objectMapper.createObjectNode();
		modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, modelName);
		modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
		description = org.apache.commons.lang3.StringUtils.defaultString(description);
		modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);

		modelData.setMetaInfo(modelObjectNode.toString());
		modelData.setName(modelName);
		modelData.setKey(org.apache.commons.lang3.StringUtils.defaultString(deploymentKey));

		repositoryService.saveModel(modelData);
		try {
			repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return repositoryService.createModelQuery().modelId(modelData.getId()).singleResult();
	}

	/**
	 * 添加条件
	 * 
	 * @param where
	 * @param condition
	 */
	private void addCondition(StringBuffer where, String condition) {
		if (where.length() == 0) {
			where.append(" WHERE");
		} else {
			where.append(" AND");
		}
		where.append(" ").append(condition).toString();
	}
}