package com.qmth.qrzk.repository.utils.docx;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.qmth.qrzk.repository.utils.General;

public class DocxReader {
	/**
	 * 临时文件夹
	 */
	private static File tmpDir = new File(General.TEMPORARY_DIR_PATH);
	private File docxFile;
	private ZipFile zipFile;
	private ResourceInfo relsInfo = null;
	private List<ResourceInfo> imgs = new ArrayList<ResourceInfo>();
	private File myTmpDir = new File(tmpDir, UUID.randomUUID().toString());
	private File wordDir = new File(myTmpDir, "word");
	private File defaultImgDir = new File(wordDir, "media");//图片默认存放的文件夹
	private ResourceInfo documentInfo = null;
	private List<ResourceInfo> footers = new ArrayList<ResourceInfo>();
	private List<ResourceInfo> headers = new ArrayList<ResourceInfo>();
	private ResourceInfo contentTypesInfo = null;
	private Map<String,DefaultContentType> dctMap = new HashMap<String, DefaultContentType>();

	public DocxReader(File docxFile){
		 this.docxFile = docxFile;
		
	}
	
	public void doRead() throws ZipException, IOException, DocumentException {
		wordDir.mkdirs();
		try{
			zipFile = new ZipFile(docxFile);
			@SuppressWarnings("unchecked")
			Enumeration<ZipEntry> entriesIter = (Enumeration<ZipEntry>) zipFile.entries();
	
			while (entriesIter.hasMoreElements()) {
				ZipEntry entry = entriesIter.nextElement();
				String entryName = entry.getName();
				if (entryName.equals("word/_rels/document.xml.rels")) {
					File relsFile = new File(myTmpDir, "word/_rels/document.xml.rels");
					copyFile(relsFile, entryName);
					relsInfo = new ResourceInfo(null, relsFile, "word/_rels/document.xml.rels", null, "document.xml.rels");
					parseRels(zipFile.getInputStream(entry));
				}else if(entryName.equals("word/document.xml")){
					File documentFile = new File(myTmpDir, "word/document.xml");
					copyFile(documentFile, entryName);
					documentInfo = new ResourceInfo(null, documentFile, "word/document.xml", null, "document");
				}else if(entryName.equals("[Content_Types].xml")){
					File contentTypesFile = new File(myTmpDir, "[Content_Types].xml");
					copyFile(contentTypesFile, entryName);
					contentTypesInfo = new ResourceInfo(null, contentTypesFile, "[Content_Types].xml", null, "Content_Types");
					parseContentTypes(zipFile.getInputStream(entry));
				}
			}
		}finally{
			General.close(zipFile);
		}
	}
	
	public File getDocxFile() {
		return docxFile;
	}

	public List<ResourceInfo> getImgs() {
		return imgs;
	}

	public ResourceInfo getDocumentInfo() {
		return documentInfo;
	}

	public List<ResourceInfo> getFooters() {
		return footers;
	}

	public List<ResourceInfo> getHeaders() {
		return headers;
	}
	
	public ResourceInfo getContentTypesInfo() {
		return contentTypesInfo;
	}
	
	public File getDefaultImgDir() {
		return defaultImgDir;
	}

	public ResourceInfo getRelsInfo() {
		return relsInfo;
	}

	public Map<String, DefaultContentType> getDefaultContentTypeMap() {
		return dctMap;
	}

	private void parseContentTypes(InputStream is) throws DocumentException{
		SAXReader reader = new SAXReader();
		Document document = reader.read(is);
		for (Object obj : document.getRootElement().elements("Default")) {
			Element ele = (Element) obj;
			String contentType = ele.attribute("ContentType").getText();
			String extension = ele.attribute("Extension").getText();
			dctMap.put(contentType, new DefaultContentType(contentType, extension));
		}
	}

	private void parseRels(InputStream is) throws DocumentException,
			IOException {
		SAXReader reader = new SAXReader();
		Document document = reader.read(is);
		for (Object obj : document.getRootElement().elements("Relationship")) {
			Element ele = (Element) obj;
			String type = ele.attribute("Type").getText();
			String id = ele.attribute("Id").getText();
			String target = ele.attribute("Target").getText();
			File targetFile = new File(wordDir, target);

			File tf = new File("word/" + target);
			File tf2 = new File("");
			String pathInZip = tf.getCanonicalPath()
					.substring(tf2.getCanonicalPath().length() + 1)
					.replace("\\", "/");

			copyFile(targetFile, pathInZip);

			if (type.equals("http://schemas.openxmlformats.org/officeDocument/2006/relationships/image")) {
				imgs.add(new ResourceInfo(id, targetFile, pathInZip, target, type));
			} else if (type.equals("http://schemas.openxmlformats.org/officeDocument/2006/relationships/header")) {
				headers.add(new ResourceInfo(id, targetFile, pathInZip, target, type));
			} else if (type.equals("http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer")) {
				footers.add(new ResourceInfo(id, targetFile, pathInZip, target, type));
			}
		}
	}

	public void copyFile(File targetFile, String pathInZip) throws IOException {
		if (!targetFile.getParentFile().exists()) {
			targetFile.getParentFile().mkdirs();
		}
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(targetFile);
			IOUtils.copyLarge(zipFile.getInputStream(zipFile.getEntry(pathInZip)), fos);
			fos.flush();
		} finally {
			General.close(fos);
		}
	}

	public void releaseTemporaries() {
		General.delete(myTmpDir);
	}

	/**
	 * 批量更新zip文件里面的内容
	 * @param docxFile zip文件，即docx文件
	 * @param needToUpdate 需要更新的内容，由于需要与docxFile里面的文件进行交叉对比，所以这样选用Map，用来提升运行效率。<p>注意:key、ResourceInfo里的PathInZip和File必须保持一致</p>
	 * @throws IOException
	 */
	public static void updateDocx(File docxFile,Map<String,ResourceInfo> needToUpdate) throws IOException {
		ZipOutputStream zos = null;
		ZipFile zipFile = null;
		File tf = null;
		try {
			tf = new File(docxFile.getParent(),UUID.randomUUID().toString());
			FileUtils.copyFile(docxFile, tf);
			zos = new ZipOutputStream(new FileOutputStream(docxFile));
			zipFile = new ZipFile(tf);
			@SuppressWarnings("unchecked")
			Enumeration<ZipEntry> entriesIter = (Enumeration<ZipEntry>) zipFile.entries();
			Map<String,ResourceInfo> tmpMap = new HashMap<String, ResourceInfo>(needToUpdate);

			FileInputStream fis = null;
			while (entriesIter.hasMoreElements()) {
					ZipEntry entry = entriesIter.nextElement();
					String entryPath = entry.getName();
					ResourceInfo info = tmpMap.get(entryPath);
					if(info != null){
						try{
							fis = new FileInputStream(info.getFile());
							zos.putNextEntry(new ZipEntry(entryPath));
							IOUtils.copy(fis, zos);
							tmpMap.remove(entryPath);
						}finally{
							General.close(fis);
						}
					}else{
						zos.putNextEntry(new ZipEntry(entryPath));
						IOUtils.copy(zipFile.getInputStream(entry), zos);
					}
			}
			for(ResourceInfo info:tmpMap.values()){
				try{
					fis = new FileInputStream(info.getFile());
					zos.putNextEntry(new ZipEntry(info.getPathInZip()));
					IOUtils.copy(fis, zos);
				}finally{
					General.close(fis);
				}
			}
			zos.closeEntry();
		}finally{
			General.close(zipFile);
			General.close(zos);
			General.delete(tf);
		}
	}

//	public static void main(String[] args) {
//		DocxReader reader = null;
//		try {
//			reader = new DocxReader(new File("E:\\temp\\pagerTmp.docx"));
//		} catch (Exception e) {
//			e.printStackTrace();
//		} finally {
//			if (reader != null) {
//				reader.releaseTemporaries();
//			}
//		}
//		try {
//			File docxFile = new File("E:\\temp\\pagerTmp.docx");
//			String entryPath = "word/media/image1.png";
//			File newEntryFile = new File("E:\\temp\\7.png");
//			updateDocx(docxFile, entryPath, newEntryFile);
//
//			entryPath = "word/media/image1.png";
//			newEntryFile = new File("E:\\temp\\5.png");
//			updateDocx(docxFile, entryPath, newEntryFile);
//			
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
//		System.out.println("运行完毕！");
//	}
}
