package cn.net.connor.std.processedit.word;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.imageio.ImageIO;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

import com.teamcenter.rac.aif.kernel.AIFComponentContext;
import com.teamcenter.rac.kernel.TCComponent;
import com.teamcenter.rac.kernel.TCComponentBOMLine;
import com.teamcenter.rac.kernel.TCComponentDataset;
import com.teamcenter.rac.kernel.TCComponentItemRevision;
import com.teamcenter.rac.kernel.TCException;
import com.teamcenter.rac.kernel.TCSession;
import com.teamcenter.rac.util.Registry;

import cn.net.connor.std.aifrcp.util.KUtil;
import cn.net.connor.std.aifrcp.util.POIUtil;
import cn.net.connor.std.aifrcp.util.PropLocation;
import cn.net.connor.std.processedit.util.ProcessUtil;
import cn.net.connor.std.processedit.word.KWordUtil.ExcelInfo;
import cn.net.connor.std.processedit.word.KWordUtil.PicInfo;
import cn.net.connor.std.processedit.word.KWordUtil.TableText;

public class KWord {

	public static final Registry REG = Registry.getRegistry(KWord.class);
	public static final String STARTROW_FLAG = "STARTROW";
	public static final String ENDROW_FLAG = "ENDROW";
	public static final String TABLEINDEX_FLAG = "TABLEINDEX";
	public static final String PICWIDTH_FLAG = "PICWIDTH";
	public static final String PICHEIGHT_FLAG = "PICHEIGHT";
	public static final String CHECKIMAGE_FLAG = "CHECKIMAGE";
	public static final String READIMAGE_FLAG = "IMAGE";
	public static final String LINEGAP_FLAG = "LINEGAP";
	public static final String FILTERTYPE_FLAG = "FILTERTYPE";
	public static final String GRANDCHILDTYPE_FLAG = "GRANDCHILDTYPE";
	public static final String GRANDCHILD_FLAG = "READGRANDCHILD";
	public static final String GRANDCHILDISTARGET_FLAG = "READGRANDCHILDASTARGET";
	public static final String WRAPLINE_FLAG = "WRAPLINE";
	public static final String SPLITLINE_FLAG = "SPLITLINE";
	public static final String MOVETOTOP_FLAG = "MOVETOTOP";
	public static final String REWRITE_LINENUM_FLAG = "REWRITELINENUM";
	public static final String REWRITE_LINENUM_PREFIX = "#LN";
	public static final String TOTALPAGECOUNTPLUG_FLAG = "ADDPAGECOUNTFROMMEOP";
	public static final String PREF_PASSWORD = "Connor_AVIC_WORD_Protect";
	public static final String APPEND_WORD_FLAG = "APPENDFILE";

	public static final String LINENUM_FLAG = "LINENUM";// 1=LINENUM
	public static final String FIELD_TOTALPAGEINDEX = REG.getString("wordfield_pageno.NAME");
	public static final String FIELD_PAGEINDEX = REG.getString("wordfield_totalpageno.NAME");
	// 105 swap
	// public static final String FIELD_PAGEINDEX =
	// REG.getString("wordfield_pageno.NAME");
	// public static final String FIELD_TOTALPAGEINDEX =
	// REG.getString("wordfield_totalpageno.NAME");
	public static final String FIELD_TOTALPAGEINDEX_MINUS_ONE = REG.getString("wordfield_totalpagenominus1.NAME");
	public static final String FIELD_TOTALPAGECOUNT = REG.getString("wordfield_pagecount.NAME");
	public static final String FIELD_PAGECOUNT = REG.getString("wordfield_totalpagecount.NAME");
	// public static final String FIELD_PAGECOUNT =
	// REG.getString("wordfield_pagecount.NAME");
	// public static final String FIELD_TOTALPAGECOUNT =
	// REG.getString("wordfield_totalpagecount.NAME");
	public static final String FIELD_TOTALPAGECOUNT_MINUS_ONE = REG.getString("wordfield_totalpagecountminus1.NAME");
	public static final String FIELD_TOTALACTUALPAGECOUNT = REG.getString("wordfield_totalactualpagecount.NAME");
	public static final String FIELD_TOTALACTUALPAGECOUNT_MINUS_ONE = REG.getString("wordfield_totalactualpagecountminus1.NAME");

	public static final String REL_REV_IMAGE = "IMAN_specification";
	public static final String REL_REV_DOC = "IMAN_specification";

	public static final String[] IMAGE_DS_TYPES = new String[] { "Image", "JPEG", "MSExcelX", "MSExcel", "PDF" };

	private String prefName;
	private TCSession session;
	private TCComponentDataset wordDataset;
	private int startRow = -1;
	private int endRow = -1;
	private int tableIndex = 1;
	private int picWidth = -1;
	private int picHeight = -1;
	private Map<String, String> textConfigMap = new HashMap<String, String>();
	private Map<Integer, String> tableConfigMap = new HashMap<Integer, String>();
	private Map<Integer, Integer> wrapLineConfigMap = new HashMap<Integer, Integer>();
	private Map<Integer, String> splitLineConfigMap = new HashMap<Integer, String>();
	private List<MoveToTopConfig> moveToTopCols = new ArrayList<MoveToTopConfig>();
	private int lineNum = 1;
	private int lineGap = 0;
	private List<PageInfo> pageInfos = new ArrayList<KWord.PageInfo>();
	private String bookMarkImage;
	private String[] filterTypes;
	private String[] grandChildTypes;
	private boolean readGrandChild = false;
	private boolean readGrandChildAsTarget = false;
	public static Map<Object, File> CACHED_FILES = new HashMap<Object, File>();
	private List<ImageExcelWordInfo> imageExcelWordInfos = new ArrayList<KWord.ImageExcelWordInfo>();
	private int[] rewriteLineNum;
	private String addTotalPageFromMEOP = null;
	private String[] appendWordConfig = null;

	public static File getFileInCache(Object key) {
		if (key == null) {
			return null;
		}
		if (CACHED_FILES.containsKey(key)) {
			File file = CACHED_FILES.get(key);
			if (file != null && file.exists()) {
				return file;
			}
		}
		return null;
	}

	public static class MoveToTopConfig {
		int col;
		int[] subCols;

		public MoveToTopConfig(int col, int[] subCols) {
			this.col = col;
			this.subCols = subCols;
		}
	}

	public static class ImageExcelWordInfo implements Comparable<ImageExcelWordInfo> {

		int[] imageBound;
		String imageDatasetNameKey;
		String sheetName;
		int sheetIndex = -1;
		String sheetRange;
		TCComponentDataset anotherWordDataset;
		int width = -1;
		int height = -1;
		File dataFile;
		String picName;

		public ImageExcelWordInfo(int[] imageBound, String imageDatasetNameKey, String sheetName, TCComponentDataset anotherWordDataset, int width, int height) {
			this.imageBound = imageBound;
			this.imageDatasetNameKey = imageDatasetNameKey;
			this.sheetName = sheetName;
			this.anotherWordDataset = anotherWordDataset;
			this.width = width;
			this.height = height;
		}

		public ImageExcelWordInfo getClone(File dataFile, int sheetIndex, String sheetRange, String picName) {
			ImageExcelWordInfo info = new ImageExcelWordInfo(imageBound, imageDatasetNameKey, sheetName, anotherWordDataset, width, height);
			info.dataFile = dataFile;
			info.sheetIndex = sheetIndex;
			info.sheetRange = sheetRange;
			info.picName = picName;
			return info;
		}

		@Override
		public String toString() {
			return picName;
		}

		@Override
		public int compareTo(ImageExcelWordInfo o) {
			if (o == null) {
				return 1;
			}
			int n1 = getSortId(this.picName);
			int n2 = getSortId(o.picName);
			return n1 - n2;
		}

		private int getSortId(String str) {
			if (KUtil.isEmpty(str)) {
				return -1;
			}
			int n = 0;
			char[] chars = str.toCharArray();
			for (char c : chars) {
				if (Character.isDigit(c)) {
					n = n * 10 + (c - '0');
				} else {
					if (n > 0) {
						break;
					}
				}
			}
			// System.out.println(str+" -> "+n);
			return n;
		}
	}

	public static class PageInfo {

		Map<String, String> textMap;
		List<TableText> tableData;
		List<PicInfo> picInfo;
		List<ExcelInfo> excelInfo;
		TCComponentDataset dataset;
		File templateFile;
		File wordFile;

		public PageInfo(TCComponentDataset dataset, Map<String, String> textMap, List<TableText> tableData, List<PicInfo> picInfo, List<ExcelInfo> excelInfo) {
			this.textMap = textMap;
			this.tableData = tableData;
			this.picInfo = picInfo;
			this.excelInfo = excelInfo;
			this.dataset = dataset;
		}

		public File getTemplateFile(File dir) throws Exception {
			this.templateFile = getFileInCache(dataset);
			if (templateFile != null) {
				return templateFile;
			}
			this.templateFile = KUtil.getDatasetFile(dir, dataset);
			if (templateFile == null || !templateFile.exists()) {
				throw new Exception("Download file from " + dataset + " failed.");
			}
			return templateFile;
		}

		public void clearCache() {
			if (templateFile != null) {
				templateFile.delete();
				templateFile = null;
			}
			if (wordFile != null) {
				wordFile.delete();
				wordFile = null;
			}
			if (picInfo != null) {
				for (PicInfo p : picInfo) {
					if (p.picFile != null) {
						p.picFile.delete();
					}
				}
				picInfo.clear();
				picInfo = null;
			}
			if (excelInfo != null) {
				for (ExcelInfo p : excelInfo) {
					p.excelFile.delete();
				}
				excelInfo.clear();
				excelInfo = null;
			}
		}
	}

	public static void export2(TCSession session, List<KWord> kWords, String dsName, TCComponentBOMLine datasetLine, List<TCComponentBOMLine> targetLines, List<TCComponentItemRevision> meTargetRevs)
			throws Exception {
		export2(session, kWords, dsName, datasetLine, targetLines, meTargetRevs, true);
	}

	/**
	 * Export bom data to word
	 * 
	 * @param session
	 * @param kWords
	 *            page config beans
	 * @param dsName
	 *            dataset name
	 * @param targetLine
	 *            target BOM line
	 * @param meTargetRev
	 *            IMAN_METarget
	 * @param item2Types
	 *            split child BOM lines with different types, property location will
	 *            be marked with 2
	 * @throws Exception
	 */
	public static void export2(TCSession session, List<KWord> kWords, String dsName, TCComponentBOMLine datasetLine, List<TCComponentBOMLine> targetLines, List<TCComponentItemRevision> meTargetRevs,
			boolean addPassword) throws Exception {
		export(session, kWords, dsName, datasetLine == null ? null : datasetLine.getItemRevision(), targetLines, meTargetRevs, addPassword, null, null);
	}

	public static void export(TCSession session, List<KWord> kWords, String dsName, TCComponentItemRevision datasetRev, List<TCComponentBOMLine> targetLines,
			List<TCComponentItemRevision> meTargetRevs, boolean addPassword, Map<String, String> fixTextMap, File exportFile) throws Exception {
		if (kWords.size() == 0) {
			throw new Exception(REG.getString("noexportdata.ERROR"));
		}
		String password = addPassword ? KUtil.getPrefVal(session, PREF_PASSWORD) : null;
		String prefix = String.format("[%s: %s] ", REG.getString("export"), dsName);
		String tempDir = System.getenv("TEMP") + "\\" + dsName + "_" + UUID.randomUUID();
		File dir = new File(tempDir);
		if (dir.mkdirs()) {
			System.out.println("Temp folder: " + tempDir);
		} else {
			throw new Exception("Create folder failed: " + tempDir);
		}
		List<File> files = new ArrayList<File>();
		int totalPageCnt = 0;
		int totalActualPageCnt = 0;
		session.setStatus(prefix + REG.getString("preparedata"));
		// prepare data
		Map<File, String[]> appendWords = new LinkedHashMap<File, String[]>();
		int size = kWords.size();
		for (int i = 0; i < size; i++) {
			// format marked data
			KWord kWord = kWords.get(i);
			TCComponentBOMLine targetLine = targetLines.get(i);
			Map<String, TCComponent> markedComponents = new HashMap<String, TCComponent>();
			List<Map<String, TCComponent>> markedLineComponents = new ArrayList<Map<String, TCComponent>>();
			Map<TCComponent, List<TCComponent>> relatedComponents = new HashMap<TCComponent, List<TCComponent>>();
			targetLine.refresh();
			PropLocation2.formatMarkedData2(targetLine, meTargetRevs.get(i), kWord.filterTypes, kWord.readGrandChild, kWord.readGrandChildAsTarget, kWord.grandChildTypes, markedComponents,
					markedLineComponents, relatedComponents);
			session.setStatus(prefix + REG.getString("preparedata") + " ( " + (i + 1) + " / " + size + " )");
			// add target info to markedLineComponents
			{
				Map<String, TCComponent> lineComps;
				if (markedLineComponents.size() == 0) {
					lineComps = new HashMap<String, TCComponent>();
					markedLineComponents.add(lineComps);
				} else {
					lineComps = markedLineComponents.get(0);
				}
				for (String key : markedComponents.keySet()) {
					lineComps.put("T" + key, markedComponents.get(key));
				}
			}
			if (markedComponents.containsKey(PropLocation.METARGET_REV)) {
				TCComponent mrev = markedComponents.get(PropLocation.METARGET_REV);
				if (mrev != null) {
					for (Map<String, TCComponent> map : markedLineComponents) {
						map.put(PropLocation.METARGET_REV, mrev);
					}
				}
			}
			if (kWord.wrapLineConfigMap.size() > 0 || kWord.splitLineConfigMap.size() > 0) {
				kWord.prepareExportDataWrap(dir, markedComponents, markedLineComponents, relatedComponents);
			} else {
				kWord.prepareExportData(dir, markedComponents, markedLineComponents, relatedComponents);
			}
			totalPageCnt += kWord.getPageCnt();
			totalActualPageCnt += kWord.getPageCnt();
			markedComponents.clear();
			markedComponents = null;
			markedLineComponents.clear();
			markedLineComponents = null;
			// append word
			if (KUtil.getLen(kWord.appendWordConfig) > 0 && !KUtil.isEmpty(kWord.appendWordConfig[0])) {
				System.out.println("Append Word: " + targetLine);
				// if (isMEOP(targetLine.getItem().getType())) {
				TCComponentItemRevision rev = targetLine.getItemRevision();
				System.out.println("read page: " + rev + " -> " + Arrays.toString(kWord.appendWordConfig));
				List<TCComponent> datasets = KUtil2.getChildComponentsContainsStr(rev, "MSWordX", KWord.REL_REV_DOC, kWord.appendWordConfig[0]);
				System.out.println(">> find datasets: " + datasets);
				int dsCnt = datasets == null ? 0 : datasets.size();
				for (int k = 0; k < dsCnt; k++) {
					TCComponent appendDs = datasets.get(k);
					int pCnt = KWordUtil.getPageCntAndFile((TCComponentDataset) appendDs, appendWords, kWord.appendWordConfig, dir);
					System.out.println(">> Append Page Count: " + pCnt);
					if (pCnt > 0) {
						totalPageCnt += pCnt;
						totalActualPageCnt += pCnt;
					}
				}
				// }
			}
			// add total page count from other datasets
			if (kWord.addTotalPageFromMEOP != null) {
				System.out.println("Add Meop Page Count: " + targetLine);
				AIFComponentContext[] cc = targetLine.getChildren();
				int len = KUtil.getLen(cc);
				for (int j = 0; j < len; j++) {
					TCComponentBOMLine cLine = (TCComponentBOMLine) cc[j].getComponent();
					if (ProcessUtil.isMEOP(session, cLine.getItem().getType())) {
						TCComponentItemRevision rev = cLine.getItemRevision();
						System.out.println("read page count: " + rev + " -> " + kWord.addTotalPageFromMEOP);
						List<TCComponent> datasets = KUtil2.getChildComponentsContainsStr(rev, "MSWordX", KWord.REL_REV_DOC, kWord.addTotalPageFromMEOP);
						System.out.println(">> find datasets: " + datasets);
						TCComponent latestDataset = KUtil.getLatestComponent(datasets);
						System.out.println(">> latest: " + latestDataset);
						if (latestDataset != null) {
							int pCnt = KWordUtil.getPageCnt((TCComponentDataset) latestDataset);
							System.out.println(">> Add Page Count: " + pCnt);
							if (pCnt > 0) {
								totalPageCnt += pCnt;
							}
						}
					}
				}
			}
		}
		System.out.println("Total Page Cnt: " + totalPageCnt);
		System.out.println("Total Actual Page Cnt: " + totalActualPageCnt);
		Map<String, String> overwriteText = new HashMap<String, String>();
		if (fixTextMap != null) {
			overwriteText.putAll(fixTextMap);
		}
		overwriteText.put(KWord.FIELD_TOTALPAGECOUNT, totalPageCnt + "");
		overwriteText.put(KWord.FIELD_TOTALPAGECOUNT_MINUS_ONE, (totalPageCnt - 1) + "");
		overwriteText.put(KWord.FIELD_TOTALACTUALPAGECOUNT, totalActualPageCnt + "");
		overwriteText.put(KWord.FIELD_TOTALACTUALPAGECOUNT_MINUS_ONE, (totalActualPageCnt - 1) + "");
		if (exportFile == null) {
			String fileName = dir.getAbsolutePath() + "\\" + dsName + ".docx";
			exportFile = new File(fileName);
		}
		session.setStatus(prefix + REG.getString("writedata"));
		// export all page
		for (int i = 0; i < size; i++) {
			session.setStatus(prefix + REG.getString("writedata") + " ( " + (i + 1) + " / " + size + " )");
			KWord kWord = kWords.get(i);
			overwriteText.put(KWord.FIELD_PAGECOUNT, kWord.getPageCnt() + "");
			kWord.exportPages(dir, files, overwriteText);
		}
		// appendFile
		System.out.println("Append File: " + appendWords);
		int appendCnt = appendWords.size();
		int pageNum = files.size() + 1;
		int appendIndex = 1;
		for (File appendFile : appendWords.keySet()) {
			String[] config = appendWords.get(appendFile);
			int[] tablePos = null;
			if (KUtil.getLen(config) == 5) {
				tablePos = new int[] { Integer.parseInt(config[1]), Integer.parseInt(config[2]), Integer.parseInt(config[3]), Integer.parseInt(config[4]) };
			}
			session.setStatus(prefix + REG.getString("appendword") + " ( " + appendIndex + " / " + appendCnt + " )");
			pageNum = KWordUtil.setAppendWordPageNumAndCount(appendFile, pageNum, totalActualPageCnt, tablePos);
			files.add(appendFile);
			appendIndex++;
		}
		// for (int i = 0; i < appendCnt; i++) {
		// File appendFile = appendWords.get(i);
		// }
		// merge
		System.out.println("Merge to: " + exportFile.getAbsolutePath());
		session.setStatus(prefix + REG.getString("mergefile"));
		KWordUtil.mergeDocFiles2(exportFile, files, session, prefix + REG.getString("mergefile"), password);
		// upload file
		session.setStatus(prefix + REG.getString("uploadfile"));
		if (datasetRev != null) {
			System.out.println("Upload File to " + datasetRev);
			KUtil.uploadFile(session, datasetRev, exportFile, dsName, "MSWordX", "word", REL_REV_DOC);
			files.add(exportFile);
			datasetRev.refresh();
			exportFile.delete();
		}
		session.setStatus(prefix + REG.getString("clearcache"));
		for (int i = 0; i < size; i++) {
			KWord kWord = kWords.get(i);
			kWord.clearCache();
		}
		for (File file : appendWords.keySet()) {
			file.delete();
		}
		dir.delete();
		CACHED_FILES.clear();
	}

	public KWord(TCSession session, String prefName) throws Exception {
		this.session = session;
		this.prefName = prefName;
		loadConfig();
	}

	private List<ImageExcelWordInfo> readImage(File dir, Map<String, TCComponent> markedComponents) throws Exception {
		List<ImageExcelWordInfo> pics = new ArrayList<KWord.ImageExcelWordInfo>();
		if (!KUtil.isEmpty(bookMarkImage) && markedComponents.containsKey(PropLocation.REV)) {
			TCComponent rev = markedComponents.get(PropLocation.REV);
			System.out.println("READ IMAGE AND EXCEL: " + rev);
			// 3D snap shot
			{
				List<TCComponentDataset> datasets = KUtil2.getDatasets(rev, "SnapShotViewData", "IMAN_3D_snap_shot");
				System.out.println("Find 3D Snap Shot: " + datasets);
				int dsCnt = datasets == null ? 0 : datasets.size();
				for (int j = 0; j < dsCnt; j++) {
					TCComponentDataset dataset = datasets.get(j);
					System.out.println(">> Add Image: " + dataset);
					String dsName = dataset.getStringProperty("object_name");
					File image = KUtil.getDatasetFile(dir, dataset, "Image");
					if (image == null) {
						continue;
					}
					List<ImageExcelWordInfo> imageWordInfo = getFitWord(image, dsName);
					if (imageWordInfo != null) {
						pics.addAll(imageWordInfo);
					} else {
						ImageExcelWordInfo newInfo = new ImageExcelWordInfo(null, null, null, wordDataset, picWidth, picHeight);
						newInfo.dataFile = image;
						newInfo.picName = dsName;
						pics.add(newInfo);
					}
				}
			}
			int len = IMAGE_DS_TYPES.length;
			for (int i = 0; i < len; i++) {
				List<TCComponentDataset> datasets = KUtil2.getDatasets(rev, IMAGE_DS_TYPES[i], REL_REV_IMAGE);
				int dsCnt = datasets == null ? 0 : datasets.size();
				for (int j = 0; j < dsCnt; j++) {
					TCComponentDataset dataset = datasets.get(j);
					System.out.println(">> Add Image or Excel or PDF: " + dataset);
					String dsName = dataset.getStringProperty("object_name");
					boolean isPDF = "PDF".equals(dataset.getType());
					if (isPDF && !dsName.startsWith("\u56FE")) {
						System.out.println(">> Skip PDF");
						continue;
					}
					File image = isPDF ? null : KUtil.getDatasetFile(dir, dataset);
					List<ImageExcelWordInfo> imageWordInfo = getFitWord(image, dsName);
					if (imageWordInfo != null) {
						pics.addAll(imageWordInfo);
						// BufferedImage bimg = ImageIO.read(image);
						// int width = bimg.getWidth();
						// int height = bimg.getHeight();
						// System.out.println(String.format("W: %d, H: %d", width, height));
						// if(width>imageBound[0]||height>imageBound[1]) {
						// pics.put(image, anotherWordDataset);
						// }else {
						// pics.put(image, wordDataset);
						// }
					} else {
						ImageExcelWordInfo newInfo = new ImageExcelWordInfo(null, null, null, wordDataset, picWidth, picHeight);
						newInfo.dataFile = image;
						newInfo.picName = dsName;
						pics.add(newInfo);
					}
				}
			}
		}
		System.out.println("All Pics: " + pics);
		Collections.sort(pics);
		System.out.println(">> sorted: " + pics);
		return pics;
	}

	private List<ImageExcelWordInfo> getFitWord(File image, String imageDsName) throws Exception {
		int cnt = this.imageExcelWordInfos == null ? 0 : this.imageExcelWordInfos.size();
		if (cnt == 0) {
			return null;
		}
		String path = image == null ? "" : image.getAbsolutePath().toLowerCase();
		if (path.endsWith(".xlsx") || path.endsWith(".xls")) {
			System.out.println("Read Excel");
			FileInputStream in = new FileInputStream(image);
			Workbook wb = WorkbookFactory.create(in);
			// XSSFWorkbook wb = new XSSFWorkbook(in);
			in.close();
			List<ImageExcelWordInfo> res = new ArrayList<KWord.ImageExcelWordInfo>();
			int sheetCnt = wb.getNumberOfSheets();
			for (int s = 0; s < sheetCnt; s++) {
				Sheet sheet = wb.getSheetAt(s);
				String sheetName = sheet.getSheetName();
				System.out.println("Find Sheet: " + sheetName);
				for (int i = 0; i < cnt; i++) {
					ImageExcelWordInfo info = this.imageExcelWordInfos.get(i);
					if (!KUtil.isEmpty(info.sheetName) && sheetName.contains(info.sheetName)) {
						int firstRowNum = sheet.getFirstRowNum();
						int lastRowNum = sheet.getLastRowNum();
						int firstColNum = 100000;
						int lastColNum = -1;
						if (firstRowNum < 0) {
							continue;
						}
						for (int r = firstRowNum; r <= lastRowNum; r++) {
							Row row = sheet.getRow(r);
							if (row == null) {
								continue;
							}
							if (row.getFirstCellNum() >= 0) {
								firstColNum = Math.min(firstColNum, row.getFirstCellNum());
							}
							if (row.getLastCellNum() >= 0) {
								lastColNum = Math.max(lastColNum, row.getLastCellNum());
							}
						}
						if (lastColNum < 0) {
							continue;
						}

						System.out.println(String.format(">> Add to page [%d, %d], [%d, %d]", firstRowNum, firstColNum, lastRowNum, lastColNum));
						String range = "";
						range += POIUtil.formatExcelCol(firstColNum + 1);
						range += ("" + (firstRowNum + 1));
						range += ":";
						range += POIUtil.formatExcelCol(lastColNum + 1);
						range += ("" + (lastRowNum + 1));
						System.out.println(">> format range: " + range);
						ImageExcelWordInfo newInfo = info.getClone(image, s, range, imageDsName);
						res.add(newInfo);
					}
				}
			}
			return res;
		} else {
			int width = -100;
			int height = -100;
			if (image != null && !image.getAbsolutePath().toLowerCase().endsWith(".emf")) {
				BufferedImage bimg = ImageIO.read(image);
				if (bimg == null) {
					System.out.println("Read File Failed: " + image.getAbsolutePath());
					throw new TCException("读取图片失败：" + imageDsName);
				}
				width = bimg.getWidth();
				height = bimg.getHeight();
			}
			System.out.println(String.format("W: %d, H: %d, Name: %s", width, height, imageDsName));
			for (int i = 0; i < cnt; i++) {
				ImageExcelWordInfo info = this.imageExcelWordInfos.get(i);
				if (info.imageBound != null) {
					if (Math.abs(width - info.imageBound[0]) <= 20 && Math.abs(height - info.imageBound[1]) <= 20) {
						List<ImageExcelWordInfo> res = new ArrayList<KWord.ImageExcelWordInfo>();
						ImageExcelWordInfo newInfo = info.getClone(image, -1, null, imageDsName);
						res.add(newInfo);
						System.out.println(">> Find Template By Size: " + newInfo.anotherWordDataset);
						return res;
					}
				} else if (!KUtil.isEmpty(info.imageDatasetNameKey)) {
					if (imageDsName.contains(info.imageDatasetNameKey)) {
						List<ImageExcelWordInfo> res = new ArrayList<KWord.ImageExcelWordInfo>();
						ImageExcelWordInfo newInfo = info.getClone(image, -1, null, imageDsName);
						res.add(newInfo);
						System.out.println(">> Find Template By Name: " + newInfo.anotherWordDataset);
						return res;
					}
				}
			}
		}
		return null;
	}

	public void prepareExportDataWrap(File dir, Map<String, TCComponent> markedComponents, List<Map<String, TCComponent>> markedLineComponents, Map<TCComponent, List<TCComponent>> relatedComponents)
			throws Exception {
		this.lineNum = 1;
		this.pageInfos.clear();
		// read Image
		List<ImageExcelWordInfo> pics = readImage(dir, markedComponents);
		// custom field
		Map<String, String> textMap = new HashMap<String, String>();
		for (String key : this.textConfigMap.keySet()) {
			textMap.put(key, KUtil2.getMarkedPropValue(this.textConfigMap.get(key), markedComponents, relatedComponents));
		}
		// read table data first
		List<Map<Integer, String>> tableRowData = new ArrayList<Map<Integer, String>>();
		if (KUtil.isEmpty(bookMarkImage)) {
			int rowCnt = markedLineComponents == null ? 0 : markedLineComponents.size();
			for (int r = 0; r < rowCnt; r++) {
				Map<String, TCComponent> markedLine = markedLineComponents.get(r);
				Map<Integer, List<String>> tableListData = new HashMap<Integer, List<String>>();
				int divideRowCnt = 0;
				for (Integer colNum : this.tableConfigMap.keySet()) {
					String config = this.tableConfigMap.get(colNum);
					String value;
					if (LINENUM_FLAG.equalsIgnoreCase(config)) {
						if (markedLine.containsKey(PropLocation.ITEM) || lineGap <= 0) {
							value = "" + (lineNum++);
						} else {
							value = "";
						}
					} else {
						value = KUtil2.getMarkedPropValue(config, markedLine, relatedComponents);
					}
					// TableText tt = new TableText(rowNum, colNum, value);
					int maxLen = -1;
					if (this.wrapLineConfigMap.containsKey(colNum)) {
						maxLen = this.wrapLineConfigMap.get(colNum);
					}
					String splitFlag = null;
					if (this.splitLineConfigMap.containsKey(colNum)) {
						splitFlag = this.splitLineConfigMap.get(colNum);
					}
					List<String> divideVals = KChineseSpliter.getDivideVals(value, maxLen, splitFlag);
					divideRowCnt = Math.max(divideRowCnt, divideVals.size());
					tableListData.put(colNum, divideVals);
				}
				for (int d = 0; d < divideRowCnt; d++) {
					Map<Integer, String> tableData = new HashMap<Integer, String>();
					for (Integer colNum : tableListData.keySet()) {
						List<String> list = tableListData.get(colNum);
						if (list.size() > 0) {
							tableData.put(colNum, list.remove(0));
						}
					}
					tableRowData.add(tableData);
				}
				// add gap
				if (r != rowCnt - 1 && lineGap > 0) {
					Map<String, TCComponent> nextItem = markedLineComponents.get(r + 1);
					boolean addGap = (nextItem != null && nextItem.containsKey(PropLocation.ITEM));
					for (int j = 0; j < lineGap; j++) {
						if (addGap) {
							tableRowData.add(null);
						}
					}
				}
			}
		}
		// move data to top first not null row
		moveToTop(tableRowData);
		// 20200922 rewrite line numbers
		int newLineNum = 1;
		if (rewriteLineNum != null && rewriteLineNum.length == 2) {
			int dataCol = rewriteLineNum[0];
			int lineNumCol = rewriteLineNum[1];
			int rowCnt = tableRowData.size();
			for (int i = 0; i < rowCnt; i++) {
				Map<Integer, String> rowData = tableRowData.get(i);
				if (rowData == null) {
					continue;
				}
				if (rowData.containsKey(lineNumCol)) {
					rowData.remove(lineNumCol);
				}
				if (rowData.containsKey(dataCol)) {
					String value = rowData.get(dataCol);
					if (value.startsWith(REWRITE_LINENUM_PREFIX)) {
						value = value.substring(REWRITE_LINENUM_PREFIX.length());
						rowData.put(dataCol, value);
						if (!KUtil.isEmpty(value)) { // 20210531 kk 遇到空值不进行行号重写
							rowData.put(lineNumCol, (newLineNum++) + "");
						}
					}
				}
			}
		}
		List<List<Map<Integer, String>>> groupedTextList = KUtil2.groupListWithGapAdded(tableRowData, startRow <= 0 ? -1 : endRow - startRow + 1, null);
		// custom table. no image if word file has table
		int pageCnt = groupedTextList == null ? 0 : groupedTextList.size();
		int picCnt = pics == null ? 0 : pics.size();
		if (pageCnt == 0 || !KUtil.isEmpty(bookMarkImage)) {
			if (picCnt == 0) {
				// this.pageInfos.add(new PageInfo(wordDataset, textMap, null, null));
				// 20200813 don't gen page if image page has no image
				if (KUtil.isEmpty(bookMarkImage)) {
					this.pageInfos.add(new PageInfo(wordDataset, textMap, null, null, null));
				}
			} else {
				for (ImageExcelWordInfo info : pics) {
					if (info.dataFile == null && KUtil.isEmpty(info.picName)) {
						continue;
					}
					if (!KUtil.isEmpty(info.sheetName)) {
						List<ExcelInfo> excelInfo = new ArrayList<KWordUtil.ExcelInfo>();
						excelInfo.add(new ExcelInfo(info.sheetIndex, bookMarkImage, info.dataFile, info.sheetRange));
						this.pageInfos.add(new PageInfo(info.anotherWordDataset, textMap, null, null, excelInfo));
					} else {
						List<PicInfo> picInfo = new ArrayList<KWordUtil.PicInfo>();
						picInfo.add(new PicInfo(info.width, info.height, bookMarkImage, info.dataFile, info.picName));
						this.pageInfos.add(new PageInfo(info.anotherWordDataset, textMap, null, picInfo, null));
					}
				}
			}
		} else {
			for (int i = 0; i < pageCnt; i++) {
				List<TableText> tableData = new ArrayList<TableText>();
				List<Map<Integer, String>> textData = groupedTextList.get(i);
				int rowCnt = textData == null ? 0 : textData.size();
				for (int r = 0; r < rowCnt; r++) {
					Map<Integer, String> dataMap = textData.get(r);
					if (dataMap == null) {
						continue;// GAP Line
					}
					for (Integer colNum : dataMap.keySet()) {
						int rowNum = this.startRow + r;
						String value = dataMap.get(colNum);
						// if(colNum == lineNumCol) {
						// value = "" + (lineNum++);
						// }
						TableText tt = new TableText(rowNum, colNum, value);
						tableData.add(tt);
					}
				}
				this.pageInfos.add(new PageInfo(wordDataset, textMap, tableData, null, null));
			}
		}
	}

	public void moveToTop(List<Map<Integer, String>> tableRowData) {
		// move data to top first not null row
		int colCnt = this.moveToTopCols.size();
		if (colCnt <= 0) {
			return;
		}
		int rowCnt = tableRowData.size();
		if (rowCnt == 0) {
			return;
		}
		for (int i = 0; i < colCnt; i++) {
			MoveToTopConfig config = this.moveToTopCols.get(i);
			int colNum = config.col;
			int[] subCols = config.subCols;
			System.out.println("Reformat Col: " + colNum + " - " + Arrays.toString(subCols));
			for (int r = 1; r < rowCnt; r++) {
				Map<Integer, String> rowData = tableRowData.get(r);
				if (rowData == null) {
					continue;
				}
				if (rowData.containsKey(colNum)) {
					String value = rowData.get(colNum);
					if (!KUtil.isEmpty(value)) {
						// System.out.println(">> move value["+r+"]: "+value);
						// move value to top
						Map<Integer, String> targetRowData = null;
						for (int r2 = r - 1; r2 >= 0; r2--) {
							Map<Integer, String> checkRowData = tableRowData.get(r2);
							if (checkRowData == null) {
								break;
							}
							String checkValue = checkRowData.get(colNum);
							if (KUtil.isEmpty(checkValue)) {
								// System.out.println(">> to "+r2);
								targetRowData = checkRowData;
							} else {
								break;
							}
						}
						// move
						if (targetRowData != null) {
							targetRowData.put(colNum, rowData.remove(colNum));
							if (subCols != null) {
								for (int subCol : subCols) {
									if (subCol == colNum) {
										continue;
									}
									targetRowData.put(subCol, rowData.remove(subCol));
								}
							}
						}
					}
				}
			}
		}
		// remove empty row
		for (int r = rowCnt - 1; r >= 0; r--) {
			Map<Integer, String> rowData = tableRowData.get(r);
			if (rowData == null) {
				continue;
			}
			boolean empty = true;
			for (String str : rowData.values()) {
				if (!KUtil.isEmpty(str)) {
					empty = false;
					break;
				}
			}
			if (empty) {
				System.out.println("Remove Empty Row: " + r);
				tableRowData.remove(r);
			}
		}
	}

	public void prepareExportData(File dir, Map<String, TCComponent> markedComponents, List<Map<String, TCComponent>> markedLineComponents, Map<TCComponent, List<TCComponent>> relatedComponents)
			throws Exception {
		this.lineNum = 1;
		this.pageInfos.clear();
		// read Image
		List<ImageExcelWordInfo> pics = readImage(dir, markedComponents);
		// custom field
		Map<String, String> textMap = new HashMap<String, String>();
		for (String key : this.textConfigMap.keySet()) {
			textMap.put(key, KUtil2.getMarkedPropValue(this.textConfigMap.get(key), markedComponents, relatedComponents));
		}
		// custom table. no image if word file has table
		List<List<Map<String, TCComponent>>> groupedMarkedLineComponents = KUtil2.groupList(markedLineComponents, startRow <= 0 ? -1 : endRow - startRow + 1, lineGap, null, PropLocation.ITEM);
		int pageCnt = groupedMarkedLineComponents == null ? 0 : groupedMarkedLineComponents.size();
		int picCnt = pics == null ? 0 : pics.size();
		if (pageCnt == 0 || !KUtil.isEmpty(bookMarkImage)) {
			if (picCnt == 0) {
				// this.pageInfos.add(new PageInfo(wordDataset, textMap, null, null));
				// 20200813 don't gen page if image page has no image
				if (KUtil.isEmpty(bookMarkImage)) {
					this.pageInfos.add(new PageInfo(wordDataset, textMap, null, null, null));
				}
			} else {
				for (ImageExcelWordInfo info : pics) {
					if (info.dataFile == null && KUtil.isEmpty(info.picName)) {
						continue;
					}
					if (!KUtil.isEmpty(info.sheetName)) {
						List<ExcelInfo> excelInfo = new ArrayList<KWordUtil.ExcelInfo>();
						excelInfo.add(new ExcelInfo(info.sheetIndex, bookMarkImage, info.dataFile, info.sheetRange));
						this.pageInfos.add(new PageInfo(info.anotherWordDataset, textMap, null, null, excelInfo));
					} else {
						List<PicInfo> picInfo = new ArrayList<KWordUtil.PicInfo>();
						picInfo.add(new PicInfo(info.width, info.height, bookMarkImage, info.dataFile, info.picName));
						this.pageInfos.add(new PageInfo(info.anotherWordDataset, textMap, null, picInfo, null));
					}
				}
			}
		} else {
			for (int i = 0; i < pageCnt; i++) {
				List<TableText> tableData = new ArrayList<TableText>();
				List<Map<String, TCComponent>> markedLines = groupedMarkedLineComponents.get(i);
				int rowCnt = markedLines == null ? 0 : markedLines.size();
				for (int r = 0; r < rowCnt; r++) {
					Map<String, TCComponent> markedLine = markedLines.get(r);
					if (markedLine == null) {
						continue;// GAP Line
					}
					int rowNum = this.startRow + r;
					TableText lineNumText = null;
					boolean hasData = false;
					for (Integer colNum : this.tableConfigMap.keySet()) {
						String config = this.tableConfigMap.get(colNum);
						if (LINENUM_FLAG.equalsIgnoreCase(config)) {
							if (markedLine.containsKey(PropLocation.ITEM) || lineGap <= 0) {
								// value = "" + (lineNum++);
								lineNumText = new TableText(rowNum, colNum, "" + lineNum);
							}
						} else {
							String value = KUtil2.getMarkedPropValue(config, markedLine, relatedComponents);
							if (!KUtil.isEmpty(value)) {
								hasData = true;
							}
							TableText tt = new TableText(rowNum, colNum, value);
							tableData.add(tt);
						}
					}
					if (lineNumText != null && hasData) {
						lineNum++;
						tableData.add(lineNumText);
					}
				}
				this.pageInfos.add(new PageInfo(wordDataset, textMap, tableData, null, null));
			}
		}
	}

	private void clearCache() {
		int pageCnt = this.pageInfos.size();
		for (int i = 0; i < pageCnt; i++) {
			this.pageInfos.get(i).clearCache();
		}
	}

	public void exportPages(File dir, List<File> files, Map<String, String> overwriteText) throws Exception {
		if (dir == null || !dir.isDirectory()) {
			throw new Exception("Directory not found.");
		}
		if (files == null) {
			throw new Exception("'List<File> files' can't be null");
		}
		int pageCnt = this.pageInfos.size();
		for (int i = 0; i < pageCnt; i++) {
			exportPage((i + 1), dir, files, overwriteText, this.pageInfos.get(i));
		}
	}

	private void exportPage(int pageNum, File dir, List<File> files, Map<String, String> overwriteText, PageInfo pageInfo) throws Exception {
		if (pageInfo == null) {
			return;
		}
		File templateFile = pageInfo.getTemplateFile(dir);
		if (pageInfo.textMap == null) {
			pageInfo.textMap = new HashMap<String, String>();
		}
		if (overwriteText != null) {
			for (String key : overwriteText.keySet()) {
				pageInfo.textMap.put(key, overwriteText.get(key));
			}
		}
		String pageIndex = pageNum > 0 ? (pageNum + "") : ((files.size() + 1) + "");
		int totalPageIndex = files.size() + 1;
		System.out.println("---------------------------------------------------------------");
		System.out.println("Export page " + pageIndex);
		pageInfo.textMap.put(FIELD_PAGEINDEX, pageIndex);
		pageInfo.textMap.put(FIELD_TOTALPAGEINDEX, totalPageIndex + "");
		pageInfo.textMap.put(FIELD_TOTALPAGEINDEX_MINUS_ONE, (totalPageIndex - 1) + "");
		String newFile = dir.getAbsolutePath() + "\\TEMP_" + files.size() + "_" + UUID.randomUUID() + ".docx";
		pageInfo.wordFile = new File(newFile);
		KWordUtil.writeToWord(templateFile, pageInfo.wordFile, pageInfo.textMap, pageInfo.tableData, tableIndex, pageInfo.picInfo, pageInfo.excelInfo);
		files.add(pageInfo.wordFile);
		System.out.println("Export page " + pageIndex + " complete");
	}

	private void loadConfig() throws Exception {
		// all numbers start with 1 TODO version
		System.out.println("Version 20201029 ---------------------------------------------------------------");
		System.out.println("Start Load Pref: " + prefName);
		String[] prefVals = KUtil.getPrefVals(session, prefName);
		int len = KUtil.getLen(prefVals);
		if (len == 0) {
			throw new Exception(String.format(REG.getString("prefnotconfiged.ERROR1"), prefName));
		}
		String dsUid = prefVals[0];
		if (dsUid.toUpperCase().startsWith("CONNOR_")) {
			prefName = dsUid;
			System.out.println(">> Refer to: " + prefName);
			prefVals = KUtil.getPrefVals(session, prefName);
			len = KUtil.getLen(prefVals);
			if (len == 0) {
				throw new Exception(String.format(REG.getString("prefnotconfiged.ERROR1"), prefName));
			}
			dsUid = prefVals[0];
		}
		TCComponent dataset = null;
		try {
			dataset = session.stringToComponent(dsUid);
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println(String.format(">> Template Dataset: [%s], type: [%s]", dataset, (dataset == null ? "" : dataset.getType())));
		if (dataset == null || !"MSWordX".equals(dataset.getType())) {
			throw new Exception(String.format(REG.getString("preferror.ERROR1"), prefName));
		}
		this.wordDataset = (TCComponentDataset) dataset;
		for (int i = 1; i < len; i++) {
			String config = prefVals[i];
			System.out.println("Parse Config: " + config);
			int ind = config.indexOf('=');
			if (ind <= 0) {
				System.out.println(">> Skip");
				continue;
			}
			String flag = config.substring(0, ind).trim();
			String configValue = config.substring(ind + 1);
			if (STARTROW_FLAG.equalsIgnoreCase(flag)) {
				this.startRow = Integer.parseInt(configValue);
				System.out.println(">> Set Start Row: " + startRow);
			} else if (ENDROW_FLAG.equalsIgnoreCase(flag)) {
				this.endRow = Integer.parseInt(configValue);
				System.out.println(">> Set End Row: " + endRow);
			} else if (TABLEINDEX_FLAG.equalsIgnoreCase(flag)) {
				this.tableIndex = Integer.parseInt(configValue);
				System.out.println(">> Set Table Index: " + tableIndex);
			} else if (PICWIDTH_FLAG.equalsIgnoreCase(flag)) {
				this.picWidth = Integer.parseInt(configValue);
				System.out.println(">> Set Pic Width: " + picWidth);
			} else if (PICHEIGHT_FLAG.equalsIgnoreCase(flag)) {
				this.picHeight = Integer.parseInt(configValue);
				System.out.println(">> Set Pic Height: " + picHeight);
			} else if (LINEGAP_FLAG.equalsIgnoreCase(flag)) {
				this.lineGap = Integer.parseInt(configValue);
				System.out.println(">> Set Line Gap: " + lineGap);
			} else if (READIMAGE_FLAG.equalsIgnoreCase(flag)) {
				this.bookMarkImage = configValue;
				System.out.println(">> Set Read Image: " + bookMarkImage);
			} else if (FILTERTYPE_FLAG.equalsIgnoreCase(flag)) {
				this.filterTypes = configValue.split(";");
				System.out.println(">> Set Filter Types: " + Arrays.toString(filterTypes));
			} else if (GRANDCHILD_FLAG.equalsIgnoreCase(flag)) {
				this.readGrandChild = "true".equalsIgnoreCase(configValue);
				System.out.println(">> Set Read Grand Child: " + readGrandChild);
			} else if (APPEND_WORD_FLAG.equalsIgnoreCase(flag)) {
				this.appendWordConfig = configValue.split(";");
				System.out.println(">> Append Word: " + Arrays.toString(appendWordConfig));
			} else if (GRANDCHILDISTARGET_FLAG.equalsIgnoreCase(flag)) {
				this.readGrandChildAsTarget = "true".equalsIgnoreCase(configValue);
				System.out.println(">> Set Read Grand Child As Target: " + readGrandChildAsTarget);
			} else if (GRANDCHILDTYPE_FLAG.equalsIgnoreCase(flag)) {
				this.grandChildTypes = configValue.split(";");
				System.out.println(">> Set Grand Child Types: " + Arrays.toString(grandChildTypes));
			} else if (TOTALPAGECOUNTPLUG_FLAG.equalsIgnoreCase(flag)) {
				this.addTotalPageFromMEOP = configValue;
				System.out.println(">> Set Add Total Page Count From MEOP: " + addTotalPageFromMEOP);
			} else if (MOVETOTOP_FLAG.equalsIgnoreCase(flag)) {
				String[] moveConfig = configValue.split(";");
				int cnt = KUtil.getLen(moveConfig);
				for (int m = 0; m < cnt; m++) {
					String str = moveConfig[m];
					try {
						int colNum = -1;
						int[] subCols = null;
						int splitInd = str.indexOf('=');
						if (splitInd > 0) {
							colNum = Integer.parseInt(str.substring(0, splitInd));
							String[] split = str.substring(splitInd + 1).split(",");
							int splitLen = KUtil.getLen(split);
							subCols = splitLen > 0 ? new int[splitLen] : null;
							for (int n = 0; n < split.length; n++) {
								subCols[n] = Integer.parseInt(split[n].trim());
							}
						} else {
							colNum = Integer.parseInt(str);
						}
						if (colNum > 0) {
							System.out.println(">> Move To Top Column: " + colNum + " - " + Arrays.toString(subCols));
							this.moveToTopCols.add(new MoveToTopConfig(colNum, subCols));
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			} else if (REWRITE_LINENUM_FLAG.equalsIgnoreCase(flag)) {
				String[] numSplit = configValue.split(";");
				int numSplitLen = KUtil.getLen(numSplit);
				if (numSplitLen == 2) {
					try {
						int fromCol = Integer.parseInt(numSplit[0]);
						int toCol = Integer.parseInt(numSplit[1]);
						if (fromCol > 0 && toCol > 0) {
							this.rewriteLineNum = new int[] { fromCol, toCol };
							System.out.println(">> Rewrite Linenum: " + Arrays.toString(rewriteLineNum));
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			} else if (WRAPLINE_FLAG.equalsIgnoreCase(flag)) {
				String[] wrapConfig = configValue.split(";");
				int wrapCnt = KUtil.getLen(wrapConfig);
				for (int w = 0; w < wrapCnt; w++) {
					String[] wrapStr = wrapConfig[w].split("=");
					if (KUtil.getLen(wrapStr) == 2) {
						try {
							int colNum = Integer.parseInt(wrapStr[0]);
							int textCnt = Integer.parseInt(wrapStr[1]);
							if (colNum > 0 && textCnt > 0) {
								System.out.println(">> Wrap Column: " + colNum + " > " + textCnt);
								this.wrapLineConfigMap.put(colNum, textCnt);
							}
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
			} else if (SPLITLINE_FLAG.equalsIgnoreCase(flag)) {
				String[] wrapConfig = configValue.split("\\$;");
				int wrapCnt = KUtil.getLen(wrapConfig);
				for (int w = 0; w < wrapCnt; w++) {
					String[] wrapStr = wrapConfig[w].split("=");
					if (KUtil.getLen(wrapStr) == 2) {
						try {
							int colNum = Integer.parseInt(wrapStr[0]);
							String splitFlag = wrapStr[1];
							if (splitFlag != null && !"".equals(splitFlag)) {
								System.out.println(">> Split Column: " + colNum + " > " + splitFlag);
								splitFlag = splitFlag.replace("\\n", "\n").replace("\\r", "\r");
								this.splitLineConfigMap.put(colNum, splitFlag);
							}
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
			} else if (CHECKIMAGE_FLAG.equalsIgnoreCase(flag)) {
				System.out.println(">> Check Image: " + configValue);
				String[] configValueSplit = configValue.split(";");
				int configCnt = KUtil.getLen(configValueSplit);
				for (int j = 0; j < configCnt; j++) {
					String aConfigValue = configValueSplit[j];
					String[] split = aConfigValue.split("\\|");
					if (KUtil.getLen(split) == 5) {
						int w = Integer.parseInt(split[0]);
						int h = Integer.parseInt(split[1]);
						TCComponentDataset anotherWordDataset = null;
						int w2 = Integer.parseInt(split[3]);
						int h2 = Integer.parseInt(split[4]);
						try {
							anotherWordDataset = (TCComponentDataset) session.stringToComponent(split[2]);
						} catch (Exception e) {
							e.printStackTrace();
						}
						if (anotherWordDataset == null || !"MSWordX".equals(anotherWordDataset.getType())) {
							throw new Exception(String.format(REG.getString("preferror.ERROR1"), prefName));
						}
						System.out.println("Find Another Dataset: " + anotherWordDataset);
						int[] imageBound = new int[] { w, h };
						ImageExcelWordInfo info = new ImageExcelWordInfo(imageBound, null, null, anotherWordDataset, w2, h2);
						this.imageExcelWordInfos.add(info);
					} else if (KUtil.getLen(split) == 4) {
						String imageDsName = split[0];
						TCComponentDataset anotherWordDataset = null;
						int w2 = Integer.parseInt(split[2]);
						int h2 = Integer.parseInt(split[3]);
						try {
							anotherWordDataset = (TCComponentDataset) session.stringToComponent(split[1]);
						} catch (Exception e) {
							e.printStackTrace();
						}
						if (anotherWordDataset == null || !"MSWordX".equals(anotherWordDataset.getType())) {
							throw new Exception(String.format(REG.getString("preferror.ERROR1"), prefName));
						}
						System.out.println("Find Another Dataset: " + anotherWordDataset);
						ImageExcelWordInfo info = new ImageExcelWordInfo(null, imageDsName, null, anotherWordDataset, w2, h2);
						this.imageExcelWordInfos.add(info);
					} else if (KUtil.getLen(split) == 2) {
						String sheetName = split[0];
						TCComponentDataset anotherWordDataset = null;
						try {
							anotherWordDataset = (TCComponentDataset) session.stringToComponent(split[1]);
						} catch (Exception e) {
							e.printStackTrace();
						}
						if (anotherWordDataset == null || !"MSWordX".equals(anotherWordDataset.getType())) {
							throw new Exception(String.format(REG.getString("preferror.ERROR1"), prefName));
						}
						System.out.println("Find Another Dataset: " + anotherWordDataset);
						ImageExcelWordInfo info = new ImageExcelWordInfo(null, null, sheetName, anotherWordDataset, -1, -1);
						this.imageExcelWordInfos.add(info);
					}
				}
			} else {
				int num = -1;
				try {
					num = Integer.parseInt(flag);
				} catch (Exception e) {
				}
				if (num > 0) {
					this.tableConfigMap.put(num, configValue);
				} else {
					this.textConfigMap.put(flag, configValue);
				}
			}
		}
		System.out.println("Load Pref Complete: " + prefName);
		System.out.println("---------------------------------------------------------------");
	}

	public TCComponentDataset getTemplateDataset() {
		return this.wordDataset;
	}

	public int getPageCnt() {
		return this.pageInfos.size();
	}

	public static void main(String[] args) {
		try {
			KWordUtil.startWord();
			Map<String, String> textMap = new HashMap<String, String>();
			textMap.put("Test", "啦啦啦1啦啦啦");
			textMap.put("测试", "啦啦啦2啦啦啦");
			textMap.put("test3", "啦啦啦\n啦啦啦");
			List<TableText> tableData = new ArrayList<TableText>();
			for (int i = 0; i < 11; i++) {
				for (int j = 0; j < 11; j++) {
					tableData.add(new TableText(i, j, i + "-" + j));
				}
			}
			File wordFile = new File("C:\\proccard.docx");
			KWordUtil.writeToWord(wordFile, null, textMap, tableData, 1, null, null);
			List<File> fileList = new ArrayList<File>();
			fileList.add(wordFile);
			fileList.add(new File("C:\\test.docx"));
			KWordUtil.mergeDocFiles(new File("C:\\merge.docs"), fileList);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			KWordUtil.closeWord();
		}
	}

}