package com.qmth.qrzk.repository.user.service.impl;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import net.sf.json.JSONObject;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;

import com.qmth.qrzk.repository.common.base.BaseDao;
import com.qmth.qrzk.repository.pager.model.PagerConstruct;
import com.qmth.qrzk.repository.service.query.AttachmentQueryParams;
import com.qmth.qrzk.repository.task.PaperTask;
import com.qmth.qrzk.repository.task.TaskBatch;
import com.qmth.qrzk.repository.user.model.Attachment;
import com.qmth.qrzk.repository.user.model.User;
import com.qmth.qrzk.repository.utils.Consts;
import com.qmth.qrzk.repository.utils.Consts.ReviewTaskStatus;
import com.qmth.qrzk.repository.utils.DateHelper;
import com.qmth.qrzk.repository.utils.General;

@Service
public class AttachmentService {

	private static final Logger log = Logger.getLogger(AttachmentService.class);
	
	@Autowired
	private BaseDao dao;
	
	@Autowired
	private PaperTaskServiceImpl paperTaskSerivce;
	
	@Autowired
	private TaskBatchImpl taskBatchService;
	
	public Attachment getAttachmentById(int attId){
		return dao.get("getAttachmentById",attId);
	}
	
	public Attachment getAttachmentByAttr(String attr){
		return dao.get("getAttachmentByAttr",attr);
	}
	
	public List<Attachment> getAttachmentByIds(String[] attIds){
		return dao.getList("getAttachmentByIds", attIds);
	}
	
	public List<Attachment> getBackupDBAttachment(AttachmentQueryParams attachmentQueryParams){
		return dao.getList("getBackupDBAttachments", attachmentQueryParams);
	}

	/**
	 * 保存文件到附件目录，并记录到数据库表里面
	 * @param is 要保存的文件对应的流
	 * @param fileName 要保存的文件名称
	 * @param userId 当前用户ID
	 * @return
	 * @throws IOException
	 */
	public Attachment storeFile(InputStream is,String fileName,int userId) throws IOException{
		File newFile = General.newAttFile();
		OutputStream os = null;
		try {
			os = new FileOutputStream(newFile);
			IOUtils.copyLarge(is, os);
			os.flush();
			
			Attachment att = new Attachment();
			att.setCreatedBy(userId);
			att.setCurName(newFile.getName());
			att.setFileLength(newFile.length());
			att.setOriginalName(fileName);
			att.setStorePath(newFile.getAbsolutePath());
			att.setStatus(Consts.AttachmentStatus.DRAFT);
			dao.insert("addAttachment", att);
			return att;
		}finally{
			General.close(os);
		}
	}
	
	/**
	 * 修改附件并保存数据库
	 * @param att
	 * @param file
	 * @param userId
	 * @return
	 * @throws IOException
	 */
	public Attachment updateAtt(Attachment att, File file, int userId){
		att.setFileLength(file.length());
		att.setStatus(Consts.AttachmentStatus.USING);
		att.setUpdatedBy(userId);
		dao.update("updateAttachment", att);
		return att;
	}
	
	/**
	 * 修改附件状态，状态值请参考：Consts.AttachmentStatus。<br/>
	 * <b>上传的附件默认为DRAFT（草稿）状态，当某条业务数据要关联这个附件时就要把这个附件的状态改成USING（使用中）；当业务数据要被物理删除时关联的附件状态要改成DELETEED（已删除）</b>
	 * @param attId
	 * @param status
	 */
	public void changeAttStatus(int attId,String status){
		Attachment att = new Attachment();
		att.setId(attId);
		att.setStatus(status);
		dao.update("updateAttachment", att);
	}
	
	/**
	 * 删除过期的草稿状态的附件
	 * @param overdueTime 过期时间
	 * @return
	 */
	public int releaseOverdueAtts(Date overdueTime){
		List<Attachment> atts = dao.getList("getOverdueAttachments", overdueTime);
		int deletedNum = 0;
		for(Attachment att:atts){
			try {
				dao.delete("deleteOverdueAttachment", att.getId());
				General.delete(new File(att.getStorePath()));
				deletedNum++;
			} catch (DataIntegrityViolationException e) {
				log.warn("此附件还被其他数据关联，不能删除，更新状态为：" + Consts.AttachmentStatus.USING, e);
				changeAttStatus(att.getId(),Consts.AttachmentStatus.USING);
			} catch (Exception e) {
				log.warn("清除过期文件：" + att.getId() + "失败，原因：" + e.getMessage(), e);
			}
		}
		return deletedNum;
	}
	
	/**
	 * 解压附件
	 * @param batchId
	 * @param outPath
	 * @param user
	 * @throws IOException
	 */
	public JSONObject unZipFile(int batchId, String outPath, User user) throws IOException{
		JSONObject result = new JSONObject();
		List<Map<String, Object>> resultList = null;
		
		File fout = null;  
		ZipEntry tempEntry = null;
		ZipEntry entry = null;
		FileOutputStream out = null;
		BufferedOutputStream bout = null;
		BufferedInputStream bin = null;
		OutputStream os = null;
		InputStream stream = null;
		
		ZipInputStream tmpZip = null;
		ZipInputStream zip = null;
		
		boolean flag = true;
		String parent = null; //输出路径（文件夹目录）
		Set<String> set = null;
		List<String> fileList = null;
        try {  
            tmpZip = new ZipInputStream(new FileInputStream(outPath),Charset.forName("GBK"));//输入源zip路径  
            zip = new ZipInputStream(new FileInputStream(outPath),Charset.forName("GBK"));//输入源zip路径  
            
            bin = new BufferedInputStream(zip);
            parent = outPath.substring(0, outPath.lastIndexOf('\\'));
            
            set = new HashSet<String>();
            List<String> list = new ArrayList<String>();
            //循环判断文件是否符合规范
            while((tempEntry = tmpZip.getNextEntry()) != null){
            	String fileName = tempEntry.getName();
            	if(tempEntry.isDirectory()){
            		flag = false;
            		result.putAll(failedResult(fileName + ":不能上传文件夹"));
            		break;
            	}
            	
            	PaperTask task = new PaperTask();
            	task.setBatchId(batchId);
            	String taskNo = "";
            	Integer preName = (fileName.substring(0, fileName.lastIndexOf('.'))).length();
            	if(preName >= 14){
            		taskNo = fileName.substring(0, 14);
            	}else if(preName < 14 || preName > 15){
            		flag = false;
            		result.putAll(failedResult(fileName + ":文件名称不符合格式!"));
            		break;
            	}
            	task.setTaskNo(taskNo);
            	//查询并判断该文件是否有效
//            	List<PaperTask> paperTasks = dao.getList("getPaperTasksByParam", task);
//            	if(paperTasks != null && paperTasks.size() > 0){
//            		flag = false;
//            		result.putAll(failedResult(taskNo + ":该任务已存在!"));
//            		break;
//            	}
            	if(list.contains(fileName)){
            		flag = false;
            		result.putAll(failedResult(fileName + "文件名重复!"));
            		break;
            	}
            	list.add(fileName);
            	set.add(taskNo);
            }
            
            //任务集合
            List<PaperTask> taskList = null;
            if(flag == true){
        		resultList = new ArrayList<Map<String, Object>>();
        		fileList = new ArrayList<String>();
        		//解压文件
            	while((entry = zip.getNextEntry()) != null && !entry.isDirectory()){
            		String fileName = entry.getName();
            		fout = new File(parent, fileName);  
            		if(!fout.exists()){  
            			(new File(fout.getParent())).mkdirs();  
            		}  
            		out = new FileOutputStream(fout);  
            		bout = new BufferedOutputStream(out);  
            		int b;  
            		while((b = bin.read()) != -1){  
            			bout.write(b);  
            		}
            		General.close(bout);
                	General.close(out);
                	
                	fileList.add(fileName);
            	}
            	
            	TaskBatch taskBatch = taskBatchService.findById(batchId);
            	taskList = new ArrayList<PaperTask>();
            	//判断文件数量是否符合规范
            	for(String key : set){
            		File[] files = searchFile(new File(parent), key);
            		if(files.length > 4){
            			flag = false;
            			result.putAll(failedResult(key + "任务下的文件数过多"));
            			break;
            		}
            		int count = 0;
            		String taskNo = "";
            		//遍历判断试卷和答题卷是否存在
            		for(int i = 0;i < files.length;i ++){
            			File file = files[i];
            			String fileName = file.getName();
            			String preName = fileName.substring(0, fileName.lastIndexOf('.'));
            			String suffix = fileName.substring(fileName.lastIndexOf('.') + 1);
            			if(preName.length() == 16 && ("docx".equals(suffix) || "doc".equals(suffix))){
            				taskNo = preName.substring(0,14);
            				count ++;
            			}else if(preName.length() == 17 && ("docx".equals(suffix) || "doc".equals(suffix)) ){
            				count ++;
            			}else if(preName.length() == 16 && !"docx".equals(suffix) && !"doc".equals(suffix) && !"cdr".equals(suffix)){
            				flag = false;
                			result.putAll(failedResult(fileName + ":文件不符合规范"));
                			break;
            			}else if(preName.length() == 17 && !"docx".equals(suffix) && !"doc".equals(suffix)){
            				flag = false;
                			result.putAll(failedResult(fileName + ":文件不符合规范"));
                			break;
            			}
            		}
            		if(flag == true){
            			if(count < 2){
            				flag = false;
            				result.putAll(failedResult(taskNo + ":试卷或者答题卷不存在"));
            				break;
            			}else if(count > 2){
            				flag = false;
            				result.putAll(failedResult(taskNo + ":试卷或者答题卷过多"));
            				break;
            			}
            		}
            		PaperTask pt = new PaperTask();
//            		pt.setPropositionTeacher(tempMap.get(pt.getPropositionLoginName()).getId());
//    				pt.setAuditTeacher(tempMap.get(pt.getAuditTeacherLoginName()).getId());
//    				pt.setPrimarySecretary(tempMap.get(pt.getPrimarySecretaryLoginName()).getId());
//    				pt.setAssistantSecretary(tempMap.get(pt.getAssistantSecretaryLoginName()).getId());
//    				pt.setTypesetter(tempMap.get(pt.getTypesetterLoginName()).getId());
    				
    				pt.setTaskNo(taskNo);
    				pt.setTitle(taskNo);
    				pt.setPaperName(taskNo);
    				pt.setBatchId(batchId);
//    				pt.setBookId(bookId);
    				pt.setPlanStartTime(taskBatch.getPlanStartTime());
    				pt.setPlanEndTime(taskBatch.getPlanEndTime());
    				
    				pt.setStatus(ReviewTaskStatus.TO_AUDIT_PAPER);
    				pt.setCreatedBy(user.getId());
    				pt.setCreatedTime(new Date());
    				pt.setUpdatedBy(user.getId());
    				pt.setUpdatedTime(new Date());
    				
//    				pt.setCurDealer(tempMap.get(pt.getAuditTeacherLoginName()).getId());
    				
    				taskList.add(pt);
            	}
            }
            
            //生成任务
            if(flag == true && taskList.size() > 0){
            	for(PaperTask task : taskList){
            		PaperTask tk = new PaperTask();
            		tk.setBatchId(batchId);
            		tk.setTaskNo(task.getTaskNo());
                	//判断任务是否存在
                	List<PaperTask> paperTasks = dao.getList("getPaperTasksByParam", tk);
                	if(paperTasks != null && paperTasks.size() > 0){
                		dao.update("updatePaperTask", task);
                	}else{
                		dao.insert("addReviewTask", task);
                		
                		PagerConstruct pager = new PagerConstruct();
                		pager.setTaskId(task.getId());
                		pager.setName(task.getTaskNo());
                		String head = Consts.DEFAULT_HEAD.replace("XXXX", DateHelper.formatDate(new Date(),"yyyy")).replace("XX", DateHelper.formatDate(new Date(),"M"));
                		pager.setHead(head);
//    		    	pager.setSkeletonId(skel.getId());
                		pager.setResourse(2);//智能组卷
                		pager.setStatus(Consts.PaperStatus.DRAFT);//草稿
                		pager.setCreatedBy(user.getId());
                		
                		dao.insert("addPagerConstruct", pager);
                	}
            	}
            }
            
            //生成附件并存入数据库
            if(flag == true){
            	Map<String, Object> map = null;
            	
        		File newFile = null;
        		
            	for(String fileName : fileList){
            		fout = new File(parent, fileName);
            		stream = new FileInputStream(fout);
                	newFile = General.newAttFile();
        			os = new FileOutputStream(newFile);
        			IOUtils.copyLarge(stream, os);
        			os.flush();
        			
        			General.close(stream);
                	General.close(os);
                	
            		Attachment att = new Attachment();
        			att.setCreatedBy(user.getId());
        			att.setCurName(newFile.getName());
        			att.setFileLength(fout.length());
        			att.setOriginalName(fileName);
        			att.setStorePath(newFile.getAbsolutePath());
        			att.setStatus(Consts.AttachmentStatus.DRAFT);
        			dao.insert("addAttachment", att);
        			
        			map = new HashMap<String, Object>();
        			PaperTask task = new PaperTask();
                	task.setBatchId(batchId);
                	String taskNo = fileName.substring(0, 14);
                	task.setTaskNo(taskNo);
        			List<PaperTask> paperTasks = dao.getList("getPaperTasksByParam", task);
        			map.put("taskId", paperTasks.get(0).getId());
        			map.put("attId", att.getId());
        			String wordType = "";
        			String suffix = fileName.substring(fileName.lastIndexOf('.') + 1);
        			if((fileName.substring(0, fileName.lastIndexOf('.'))).length() > 15){
        				String type = fileName.substring(15, 16);
        				if("N".equals(type)){
        					wordType = "DA";
        				}else if("评".equals(type)){
        					wordType = "WPJ";
        				}
        			}else{
        				if("doc".equals(suffix) || "docx".equals(suffix)){
        					wordType = "HJ";
        				}else if("cdr".equals(suffix)){
        					wordType = "ZK";
        				}
        			}
        			map.put("wordType", wordType);
        			
        			PagerConstruct pager = dao.get("findPagerConstructByTaskId", paperTasks.get(0).getId());
        			map.put("paperId", pager.getId());
        			
        			resultList.add(map);            		
            	}
            	
            }
        } catch (Exception e) {
        	e.printStackTrace();
        }finally {
        	General.close(bout);
        	General.close(out);
        	General.close(stream);
        	General.close(os);
        	General.close(bin);
        	
        	General.close(zip);
        	General.close(tmpZip);
        }
        
        //上传成功
        if(flag == true){
        	Map<String,Object> map = new HashMap<String, Object>();
    		map.put("flag", "success");
    		map.put("msg", "上传成功!");
    		map.put("resultList", resultList);
        	result.putAll(map);
        }
        return result;
	}
	
	/**
	 * 按关键字搜索文件
	 * @param folder
	 * @param keyWord
	 * @return
	 */
    public File[] searchFile(File folder, final String keyWord) {// 递归查找包含关键字的文件
 
        File[] subFolders = folder.listFiles(new FileFilter() {// 运用内部匿名类获得文件
            
            public boolean accept(File pathname) {// 实现FileFilter类的accept方法
                if (pathname.isDirectory()
                        || (pathname.isFile() && pathname.getName().toLowerCase().contains(keyWord.toLowerCase())))// 目录或文件包含关键字
                    return true;
                return false;
            }
        });
 
        List<File> result = new ArrayList<File>();// 声明一个集合
        for (int i = 0; i < subFolders.length; i++) {// 循环显示文件夹或文件
            if (subFolders[i].isFile()) {// 如果是文件则将文件添加到结果列表中
                result.add(subFolders[i]);
            } else {// 如果是文件夹，则递归调用本方法，然后把所有的文件加到结果列表中
                File[] foldResult = searchFile(subFolders[i], keyWord);
                for (int j = 0; j < foldResult.length; j++) {// 循环显示文件
                    result.add(foldResult[j]);// 文件保存到集合中
                }
            }
        }
 
        File files[] = new File[result.size()];// 声明文件数组，长度为集合的长度
        result.toArray(files);// 集合数组化
        return files;
    }
	
	protected Map<String,Object> failedResult(String msg) {
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("flag", "failed");
		map.put("msg", (msg == null)?"操作失败":msg);
		return map;
	}
	
//	/**
//	 * 数据库备份
//	 *
//	 * @throws IOException
//	 */
//	public String backupDB(String backupfilename) {
//		String returnstr = "success";
//		try {
//			// 生成备份文件名
//			Runtime rt = Runtime.getRuntime();
//			// 调用mysql的备份命令
//			Process command = rt.exec(new String[] { "sh", "-c",
//					ParamUtils.BackupDbCmd + ">" + ParamUtils.BackupDbFilePath + backupfilename + ".sql" });
//			int processComplete = command.waitFor();
//			if (processComplete == 0) {
//				// 获取附件大小
//				File backupfile = new File(ParamUtils.BackupDbFilePath + backupfilename + ".sql");
//				long fileLength = 0;
//				if (backupfile.exists()) {
//					fileLength = backupfile.getTotalSpace();
//				}
//				// 命令执行完毕后备份文件插入附件表
//				Attachment att = new Attachment();
//				att.setCurName(backupfilename);
//				att.setOriginalName(backupfilename);
//				att.setStorePath(ParamUtils.BackupDbFilePath + backupfilename + ".sql");
//				att.setStatus(Consts.AttachmentStatus.BACKUP);
//				att.setFileLength(fileLength);
//				att.setCreatedBy(0);
//				dao.insert("addAttachment", att);
//				returnstr = "success";
//			}
//		} catch (Exception e) {
//			returnstr = "fail";
//			throw new RuntimeException("执行数据库备份(命令：" + ParamUtils.BackupDbCmd + ")失败，原因：" + e.getMessage(), e);
//		}
//		return returnstr;
//	}
//
//	/**
//	 * 数据库恢复
//	 * @throws IOException
//	 */
//	public String restoreDB(String backupfilename){
//		String returnstr = "success";
//		try {
//			Runtime rt = Runtime.getRuntime();
//			// 调用mysql的恢复命令
//			Process command = rt.exec(new String[] { "sh", "-c", ParamUtils.RestoreDbCmd+"<"+ParamUtils.BackupDbFilePath + backupfilename + ".sql"});
//			int processComplete = command.waitFor();
//			if(processComplete == 0){
//				returnstr = "success";
//			}
//		} catch (Exception e) {
//			returnstr = "fail";
//			throw new RuntimeException("执行数据库恢复(命令：" + ParamUtils.RestoreDbCmd + ")失败，原因：" + e.getMessage(), e);
//		}
//		return returnstr;
//	}
//	/**
//	 * <p>备份word文件</p>
//	 * @author songyue
//	 * @date 2016-06-20
//	 */
//	public void backupFile(String filedir){
//		try {
//			FileUtils.copyDirectory(new File(ParamUtils.AttDirPath), new File(ParamUtils.BackupFilePath + filedir));
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//	}
//
//	/**
//	 * <p>恢复word文件</p>
//	 * @author songyue
//	 * @date 2016-06-20
//	 */
//	public void restoreFile(String filedir){
//		try {
//			FileUtils.copyDirectory(new File(ParamUtils.BackupFilePath + filedir),new File(ParamUtils.AttDirPath));
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//	}
	
	public String generateFilename(){
		SimpleDateFormat sf = new SimpleDateFormat("yyyymmddhhmmss");
		String backupfilename = sf.format(new Date());
		return backupfilename;
	}
	
	public int getBackupDBAttachmentCount(AttachmentQueryParams attachmentQueryParams){
		return dao.get("getBackupDBAttachmentsCount", attachmentQueryParams);
	}
}
