package com.zjrs.appcomm.template.word.util;

import com.zjrs.appcomm.template.word.exception.IllegalMarkException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture;
import org.openxmlformats.schemas.drawingml.x2006.picture.CTPictureNonVisual;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.springframework.util.ObjectUtils;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

public class POIUtil {

	/** mark的开始标记 */
	public static final String MARK_PREFIX = "${";
	/** mark的伪属性分隔标记 */
	public static final String MARK_PSEUDO = ":";
	/** mark的属性分割标记 */
	public static final String MARK_ATTRIBUTE = ".";
	/** mark的结束标记 */
	public static final String MARK_POSTFIX = "}";
	
	public enum Pseudo {
		BEGIN, END, ITEM, INDEX;
	}
	
	public static void handleDocument(XWPFDocument document, Object data) {
		List<IBodyElement> sortedParagraphAndTable = new ArrayList<>(document.getBodyElements());

		List<Integer> indexList = new ArrayList<>();

		replaceBodyElementsMarks(sortedParagraphAndTable, data, indexList);
	}

	private static final int RETURN_ACTION_OK = 0;
	private static final int RETURN_ACTION_REPEAT = 1;
	private static final int RETURN_ACTION_OVER = 2;

	private static int replaceBodyElementsMarks(List<IBodyElement> sortedParagraphAndTable, Object data, List<Integer> indexList) {
		for (int i = 0; i < sortedParagraphAndTable.size(); i++) {
			IBodyElement paragraphOrTable = sortedParagraphAndTable.get(i);
			int action = replaceBodyElementMarks(paragraphOrTable, data, indexList);
			if (action == RETURN_ACTION_REPEAT) {
				int endIndex = findRepeatBodyElements(sortedParagraphAndTable, i);
				List<IBodyElement> repeatBodyElements = new ArrayList<>();
				for (int j = endIndex; j >= i; j--) {
					repeatBodyElements.add(0, sortedParagraphAndTable.remove(j));
				}
				XWPFParagraph beginParagraph = (XWPFParagraph) repeatBodyElements.remove(0);
				XWPFParagraph endParagraph = (XWPFParagraph) repeatBodyElements.remove(repeatBodyElements.size() - 1);

				indexList.add(0);
				int repeatIndexListI = indexList.size() - 1;
				while (true) {
					List<IBodyElement> newBodyElements = copyBodyElements(repeatBodyElements, beginParagraph);
					int repeatAction = replaceBodyElementsMarks(newBodyElements, data, indexList);
					if (repeatAction == RETURN_ACTION_OVER) {
						removeBodyElements(newBodyElements);
						indexList.remove(indexList.size() - 1);
						break;
					} else {
						indexList.set(repeatIndexListI, indexList.get(repeatIndexListI) + 1);
					}
				}
				removeParagraph(endParagraph);
				removeBodyElements(repeatBodyElements);
				removeParagraph(beginParagraph);
			} else if (action == RETURN_ACTION_OVER) {
				removeBodyElements(sortedParagraphAndTable);
				return RETURN_ACTION_OVER;
			}
		}
		return RETURN_ACTION_OK;
	}
	
	private static void removeBodyElements(List<IBodyElement> bodyElements) {
		for (int i = bodyElements.size() - 1; i >= 0; i--) {
			IBodyElement bodyElement = bodyElements.get(i);
			if (bodyElement instanceof XWPFParagraph) {
				removeParagraph((XWPFParagraph) bodyElement);
			} else if (bodyElement instanceof XWPFTable) {
				removeTable((XWPFTable) bodyElement);
			}
		}
	}
	
	private static void removeParagraph(XWPFParagraph paragraph) {
		XWPFDocument document = paragraph.getDocument();
		for (int i = 0; i < document.getBodyElements().size(); i++) {
			if (document.getBodyElements().get(i).equals(paragraph)) {
				document.removeBodyElement(i);
				break;
			}
		}
	}
	
	private static void removeTable(XWPFTable table) {
		XWPFDocument document = table.getBody().getXWPFDocument();
		for (int i = 0; i < document.getBodyElements().size(); i++) {
			if (document.getBodyElements().get(i).equals(table)) {
				document.removeBodyElement(i);
				break;
			}
		}
	}
	
	private static List<IBodyElement> copyBodyElements(List<IBodyElement> bodyElements, XWPFParagraph paragraph) {
		List<IBodyElement> newBodyElements = new ArrayList<>();
		for (IBodyElement bodyElement: bodyElements) {
			if (bodyElement instanceof XWPFParagraph) {
				newBodyElements.add(copyParagraph((XWPFParagraph) bodyElement, paragraph.getCTP().newCursor()));
			} else if (bodyElement instanceof XWPFTable) {
				newBodyElements.add(copyTable((XWPFTable) bodyElement, paragraph.getCTP().newCursor()));
			}
		}
		return newBodyElements;
	}
	
	private static XWPFParagraph copyParagraph(XWPFParagraph paragraph, XmlCursor cursor) {
		XWPFParagraph newParagraph = paragraph.getDocument().insertNewParagraph(cursor);
		if (paragraph.getCTP().getPPr() != null) {
			newParagraph.getCTP().addNewPPr().set(paragraph.getCTP().getPPr().copy());
		}
		for (XWPFRun sourceRun: paragraph.getRuns()) {
			XWPFRun targetRun = newParagraph.createRun();
			targetRun.getCTR().addNewRPr().set(sourceRun.getCTR().getRPr().copy());
			targetRun.setText(getText(sourceRun), 0);
		}
		return newParagraph;
	}
	
	private static XWPFTable copyTable(XWPFTable table, XmlCursor cursor) {
		XWPFDocument doc = table.getBody().getXWPFDocument();
		XWPFTable newTable = doc.insertNewTbl(cursor);
		newTable.removeRow(0);
		newTable.getCTTbl().getTblPr().set(table.getCTTbl().getTblPr().copy());
		for (int r = 0; r < table.getRows().size(); r++) {
			XWPFTableRow row = table.getRows().get(r);
			copyRow(newTable, row, r);
		}
		return newTable;
	}
	
	private static XWPFTableRow copyRow(XWPFTable table, XWPFTableRow row, int pos) {
		XWPFTableRow newRow = table.insertNewTableRow(pos);
		if (row.getCtRow().getTrPr() != null) {
			newRow.getCtRow().addNewTrPr().set(row.getCtRow().getTrPr().copy());
		}
		for (XWPFTableCell sourceCell: row.getTableCells()) {
			XWPFTableCell targetCell = newRow.addNewTableCell();
			targetCell.getCTTc().addNewTcPr().set(sourceCell.getCTTc().getTcPr().copy());
			targetCell.removeParagraph(0);
			for (XWPFParagraph sourceParagraph: sourceCell.getParagraphs()) {
				XWPFParagraph targetParagraph = targetCell.addParagraph();
				targetParagraph.getCTP().addNewPPr().set(sourceParagraph.getCTP().getPPr().copy());
				for (XWPFRun sourceRun: sourceParagraph.getRuns()) {
					XWPFRun targetRun = targetParagraph.createRun();
					targetRun.getCTR().addNewRPr().set(sourceRun.getCTR().getRPr().copy());
					targetRun.setText(getText(sourceRun), 0);
				}
			}
		}
		return newRow;
	}
	
	private static int findRepeatBodyElements(List<IBodyElement> sortedParagraphAndTable, int beginIndex) {
		int subBeginNum = 0;
		for (int i = beginIndex + 1; i < sortedParagraphAndTable.size(); i++) {
			IBodyElement paragraphOrTable = sortedParagraphAndTable.get(i);
			if (paragraphOrTable instanceof XWPFParagraph) {
				Set<String> marks = POIUtil.findMarks((XWPFParagraph) paragraphOrTable);
				for (String mark: marks) {
					String[] markSeg = mark.split("\\" + MARK_ATTRIBUTE);
					String[] markPse = markSeg[markSeg.length - 1].split(MARK_PSEUDO);
					Pseudo pseudo = null;
					if (markPse.length >= 2) {
						try {
							pseudo = Pseudo.valueOf(markPse[1].toUpperCase());
						} catch (RuntimeException e) {
							throw new IllegalMarkException("mark伪属性未知", mark, e);
						}
					}
					if (pseudo == Pseudo.BEGIN) {
						subBeginNum++;
					} else if (pseudo == Pseudo.END) {
						if (subBeginNum == 0) {
							return i;
						} else {
							subBeginNum--;
						}
					}
				}
			}
		}
		throw new IllegalMarkException("mark伪属性BEGIN未配对", null);
	}

	private static int replaceBodyElementMarks(IBodyElement paragraphOrTable, Object data, List<Integer> indexList) {
		if (paragraphOrTable instanceof XWPFParagraph) {
			int action = replaceParagraphMarks((XWPFParagraph) paragraphOrTable, data, indexList);
			if (action == RETURN_ACTION_REPEAT) {
				return RETURN_ACTION_REPEAT;
			} else if (action == RETURN_ACTION_OVER) {
				return RETURN_ACTION_OVER;
			}
		} else if (paragraphOrTable instanceof XWPFTable) {
			replaceTableMarks((XWPFTable) paragraphOrTable, data, indexList);
		}
		return RETURN_ACTION_OK;
	}

	private static int replaceParagraphMarks(XWPFParagraph paragraph, Object data, List<Integer> indexList) {
		Set<String> marks = POIUtil.findMarks(paragraph);
		for (String mark: marks) {
			String[] markSeg = mark.split("\\" + MARK_ATTRIBUTE);
			String[] markPse = markSeg[markSeg.length - 1].split(MARK_PSEUDO);
			Pseudo pseudo = null;
			if (markPse.length >= 2) {
				try {
					pseudo = Pseudo.valueOf(markPse[1].toUpperCase());
				} catch (RuntimeException e) {
					throw new IllegalMarkException("mark伪属性未知", mark, e);
				}
			}
			if (pseudo == Pseudo.BEGIN) {
				return RETURN_ACTION_REPEAT;
			} else if (pseudo == Pseudo.END) {
				throw new IllegalMarkException("mark伪属性END未配对", mark);
			} else {
				try {
					String d = reflectData(mark, data, indexList, String.class);
					POIUtil.replaceOneText(paragraph, MARK_PREFIX + mark + MARK_POSTFIX, d);
				} catch (NeedRepeat e) {
					return RETURN_ACTION_REPEAT;
				} catch (NoMoreData e) {
					return RETURN_ACTION_OVER;
				}
			}
		}
		for (XWPFRun run: paragraph.getRuns()) {
			String mark = findPictureMark(run);
			if (mark != null) {
				byte[] d = reflectData(mark, data, indexList, byte[].class);
				try {
					replacePicture(run, d);
				} catch (InvalidFormatException e) {
					throw new RuntimeException(e);
				}
			}
		}
		return RETURN_ACTION_OK;
	}

	private static void replaceTableMarks(XWPFTable table, Object data, List<Integer> indexList) {
		int needDeleteRowTemplate = -1;
		int needDeleteRowOver = -1;
		for (int rowI = 0; rowI < table.getRows().size(); rowI++) {
			XWPFTableRow row = table.getRows().get(rowI);
			cellLine: for (XWPFTableCell cell: row.getTableCells()) {
				for (XWPFParagraph paragraph: cell.getParagraphs()) {
					int action = replaceParagraphMarks(paragraph, data, indexList);
					if (action == RETURN_ACTION_REPEAT) {
						needDeleteRowTemplate = rowI;
						indexList.add(-1);
						break cellLine;
					} else if (action == RETURN_ACTION_OVER) {
						needDeleteRowOver = rowI;
					}
				}
			}
			if (needDeleteRowTemplate != -1 && needDeleteRowOver == -1) {
				copyRow(table, table.getRows().get(needDeleteRowTemplate), rowI + 1);
				indexList.add(indexList.remove(indexList.size() - 1) + 1);
			}
		}
		if (needDeleteRowTemplate != -1) {
			table.removeRow(needDeleteRowOver);
			table.removeRow(needDeleteRowTemplate);
			indexList.remove(indexList.size() - 1);
		}
	}
	
	private static class NeedRepeat extends RuntimeException {
		private static final long serialVersionUID = 7504615595418414701L;
	}
	private static class NoMoreData extends RuntimeException {
		private static final long serialVersionUID = -3558032302829950554L;
	}

	private static <E> E reflectData(String mark, Object data, List<Integer> indexList, Class<E> clazz) {
		Object currentData = data;
		int indexI = 0;
		String[] markSeg = mark.split("\\" + MARK_ATTRIBUTE);
		for (int i = 0; i < markSeg.length; i++) {
			String markS = markSeg[i];
			String[] markPse = markS.split(MARK_PSEUDO);
			Pseudo pseudo = null;
			if (markPse.length == 2) {
				try {
					pseudo = Pseudo.valueOf(markPse[1].toUpperCase());
				} catch (RuntimeException e) {
					throw new IllegalMarkException("mark伪属性未知", mark, e);
				}
			} else if (markPse.length > 2) {
				throw new IllegalMarkException("mark伪属性个数过多", mark);
			}
			if (i == markSeg.length - 1) {
				if (pseudo == null) {
					if (String.class.equals(clazz)) {
						return (E) reflectStringData(mark, markPse[0], currentData);
					} else if (byte[].class.equals(clazz)) {
						return (E) reflectByteArrayData(mark, markPse[0], currentData);
					} else {
						throw new IllegalArgumentException("" + clazz);
					}
				} else if (pseudo == Pseudo.INDEX) {
					List<?> list = reflectListData(mark, markPse[0], currentData);
					if (indexI >= indexList.size()) {
						throw new NeedRepeat();
					}
					int listI = indexList.get(indexI);
					if (listI >= list.size()) {
						throw new NoMoreData();
					} else {
						if (String.class.equals(clazz)) {
							return (E) ((listI + 1) + "");
						} else if (byte[].class.equals(clazz)) {
							throw new IllegalMarkException("该mark不应出现在图片中", mark);
						} else {
							throw new IllegalArgumentException("" + clazz);
						}
					}
				} else if (pseudo == Pseudo.ITEM) {
					List<?> list = reflectListData(mark, markPse[0], currentData);
					if (indexI >= indexList.size()) {
						throw new NeedRepeat();
					}
					int listI = indexList.get(indexI);
					if (listI >= list.size()) {
						throw new NoMoreData();
					} else {
						if (String.class.equals(clazz)) {
							return (E) ObjectUtils.getDisplayString(reflectListItemData(list, listI, mark));
						} else if (byte[].class.equals(clazz)) {
							return (E) reflectListItemData(list, listI, mark);
						} else {
							throw new IllegalArgumentException("" + clazz);
						}
					}
				} else {
					throw new IllegalMarkException("mark伪属性错误", mark);
				}
			} else {
				if (pseudo == null) {
					currentData = reflectData(mark, markPse[0], currentData);
					if (currentData == null) {
						if (String.class.equals(clazz)) {
							return (E) "";
						} else if (byte[].class.equals(clazz)) {
							throw new IllegalMarkException("mark对应数据不能为null", mark);
						} else {
							throw new IllegalArgumentException("" + clazz);
						}
					}
				} else if (pseudo == Pseudo.INDEX) {
					throw new IllegalMarkException("错误mark", mark);
				} else if (pseudo == Pseudo.ITEM) {
					List<?> list = reflectListData(mark, markPse[0], currentData);
					int listI = -1;
					if (indexI >= indexList.size()) {
						throw new NeedRepeat();
					} else {
						listI = indexList.get(indexI);
					}
					if (listI >= list.size()) {
						throw new NoMoreData();
					} else {
						currentData = reflectListItemData(list, listI, mark);
						indexI++;
						if (currentData == null) {
							if (String.class.equals(clazz)) {
								return (E) "";
							} else if (byte[].class.equals(clazz)) {
								throw new IllegalMarkException("mark对应数据不能为null", mark);
							} else {
								throw new IllegalArgumentException("" + clazz);
							}
						}
					}
				} else {
					throw new IllegalMarkException("mark伪属性错误", mark);
				}
			}
		}
		throw new IllegalMarkException("mark遍历数据异常", mark);
	}
	
	private static Object reflectListItemData(List<?> list, int index, String mark) {
		try {
			return list.get(index);
		} catch (ArrayIndexOutOfBoundsException e) {
			System.err.println("找不到指定ListItem数据: " + mark);
			return MARK_PREFIX + mark + MARK_POSTFIX;
		}
	}
	
	private static String reflectStringData(String mark, String markS, Object data) {
		return ObjectUtils.getDisplayString(reflectData(mark, markS, data));
	}
	private static byte[] reflectByteArrayData(String mark, String markS, Object data) {
		return (byte[]) reflectData(mark, markS, data);
	}
	
	private static List<?> reflectListData(String mark, String markS, Object data) {
		Object d = reflectData(mark, markS, data);
		if (d == null) {
			return Collections.emptyList();
		} else if (d instanceof List) {
			return (List<?>) d;
		} else {
			throw new IllegalMarkException("mark对应数据类型异常", mark);
		}
	}
	
	private static Object reflectData(String mark, String markS, Object data) {
		String getMethod = "get" + NameUtil.toFirstUpperCase(markS);
		try {
			Method method = data.getClass().getMethod(getMethod);
			return method.invoke(data);
		} catch (SecurityException | NoSuchMethodException e) {
			throw new IllegalMarkException("无mark对应数据", mark, e);
		} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			throw new IllegalMarkException("获取mark对应数据异常", mark, e);
		}
	}
	
	public static String findPictureMark(XWPFRun run) {
		List<XWPFPicture> pictures = run.getEmbeddedPictures();
		if (pictures.isEmpty() || pictures.get(0) == null) {
			return null;
		}
		CTPicture picture = pictures.get(0).getCTPicture();
		if (picture == null) {
			return null;
		}
		CTPictureNonVisual pictureNonVisual = picture.getNvPicPr();
		if (pictureNonVisual == null) {
			return null;
		}
		CTNonVisualDrawingProps nonVisualDrawingProps = pictureNonVisual.getCNvPr();
		if (nonVisualDrawingProps == null) {
			return null;
		}
		String name = nonVisualDrawingProps.getName();
		if (name != null && name.startsWith(MARK_PREFIX) && name.endsWith(MARK_POSTFIX)) {
			return name.substring(MARK_PREFIX.length(), name.length() - MARK_POSTFIX.length());
		} else {
			return null;
		}
	}
	
	public static Set<String> findMarks(XWPFParagraph paragraph) {
		Set<String> markSet = new HashSet<>();
		String text = getText(paragraph);
		int index = 0;
		while (true) {
			int preIndex = text.indexOf(MARK_PREFIX, index);
			if (preIndex < 0) {
				break;
			}
			int postIndex = text.indexOf(MARK_POSTFIX, preIndex + MARK_PREFIX.length());
			if (postIndex < 0) {
				break;
			}
			String mark = text.substring(preIndex + MARK_PREFIX.length(), postIndex);
			preIndex = mark.lastIndexOf(MARK_PREFIX);
			if (preIndex >= 0) {
				mark = mark.substring(preIndex + MARK_PREFIX.length());
			}
			if (mark.length() > 0) {
				markSet.add(mark);
			}
			index = postIndex + MARK_POSTFIX.length();
		}
		return markSet;
	}
	
	public static boolean replaceOneText(XWPFParagraph paragraph, String key, String value) {
		/* 没有可替换的数据时直接返回 */
		String paragraphText = getText(paragraph);
		int index = paragraphText.indexOf(key);
		if (index < 0) {
			return false;
		}
		
		List<XWPFRun> runs = paragraph.getRuns();
		int runBeginIndex = -1;	// 需要替换的字符串所在的起始run
		int runBeginStrIndex = -1;	// 需要替换你的字符串所在起始run的下标
		int runEndIndex = -1;	// 需要替换的字符串所在的结束run
		int runEndStrIndex = -1;	// 需要替换的字符串所在结束run的下标
		
		int runIndexT = 0;	// 遍历run的临时下标
		int runStrLengthT = 0;	// 当前共遍历的字符串长度
		for (; runIndexT < runs.size(); runIndexT++) {
			int lastRunStrLengthT = runStrLengthT;
			runStrLengthT += getText(runs.get(runIndexT)).length();
			/* 找到需要替换的字符串后记录其所在的位置 */
			if (runStrLengthT > index) {
				runBeginIndex = runIndexT;
				runBeginStrIndex = index - lastRunStrLengthT;
				/* 当起始run与结束run在同一个run时，则记录结束run的位置 */
				if (runStrLengthT >= index + key.length()) {
					runEndIndex = runIndexT;
					runEndStrIndex = runBeginStrIndex + key.length();
				}
				break;
			}
		}
		/* 当起始run与结束run不在同一个run时，继续进行寻找 */
		if (runEndIndex == -1) {
			for (runIndexT++; runIndexT < runs.size(); runIndexT++) {
				int lastRunStrLengthT = runStrLengthT;
				runStrLengthT += getText(runs.get(runIndexT)).length();
				/* 找到需要替换的字符串的结束run的位置 */
				if (runStrLengthT >= index + key.length()) {
					runEndIndex = runIndexT;
					runEndStrIndex = index + key.length() - lastRunStrLengthT;
					break;
				}
			}
		}
		
		if (runBeginIndex == runEndIndex) {
			/* 当需要替换的字符串的起始run与结束run在同一个run时，则直接进行替换 */
			XWPFRun run = runs.get(runBeginIndex);
			String runText = getText(run);
			runText = runText.substring(0, runBeginStrIndex) + value + runText.substring(runEndStrIndex);	// TODO: 替换换行数据时，第二行存在行首空格
			run.setText(runText, 0);
		} else {
			/* 当需要替换的字符串的起始run与结束run不在同一个run时，则起始run进行替换，后续run均进行删除 */
			XWPFRun runBegin = runs.get(runBeginIndex);
			String runBeginText = getText(runBegin);
			runBeginText = runBeginText.substring(0, runBeginStrIndex) + value;	// TODO: 替换换行数据时，第二行存在行首空格
			runBegin.setText(runBeginText, 0);
			
			XWPFRun runEnd = runs.get(runEndIndex);
			String runEndText = getText(runEnd);
			runEndText = runEndText.substring(runEndStrIndex);
			runEnd.setText(runEndText, 0);
			
			/* 删除多于的run */
			for (int i = runEndIndex - 1; i > runBeginIndex; i--) {
				paragraph.removeRun(i);
			}
		}
		
		return true;
	}
	
	private static void replacePicture(XWPFRun run, byte[] picture) throws InvalidFormatException {
		String id = run.getDocument().addPictureData(picture, getPictureType(picture));
		
		CTR ctr = run.getCTR();
		if (ctr == null) {
			return;
		}
		List<CTDrawing> drawingList = ctr.getDrawingList();
		if (drawingList.isEmpty() || drawingList.get(0) == null) {
			return;
		}
		List<CTInline> inlineList = drawingList.get(0).getInlineList();
		if (inlineList.isEmpty() || inlineList.get(0) == null) {
			return;
		}
		CTGraphicalObject graphic = inlineList.get(0).getGraphic();
		if (graphic == null) {
			return;
		}
		CTGraphicalObjectData graphicalData = graphic.getGraphicData();
		if (graphicalData == null) {
			return;
		}
		Node graphicalDataNode = graphicalData.getDomNode();
		if (graphicalDataNode == null) {
			return;
		}
		for (int graphicalDataChildIndex = 0; graphicalDataChildIndex < graphicalDataNode.getChildNodes().getLength(); graphicalDataChildIndex++) {
			Node graphicalDataChild = graphicalDataNode.getChildNodes().item(graphicalDataChildIndex);
			if (graphicalDataChild.getNodeName().equals("pic:pic")) {
				for (int picChildIndex = 0; picChildIndex < graphicalDataChild.getChildNodes().getLength(); picChildIndex++) {
					Node picChild = graphicalDataChild.getChildNodes().item(picChildIndex);
					if (picChild.getNodeName().equals("pic:blipFill")) {
						for (int blipFillChildIndex = 0; blipFillChildIndex < picChild.getChildNodes().getLength(); blipFillChildIndex++) {
							Node blipFillChild = picChild.getChildNodes().item(blipFillChildIndex);
							if (blipFillChild.getNodeName().equals("a:blip")) {
								NamedNodeMap blipAttributes = blipFillChild.getAttributes();
								Node blipAttributesNode = blipAttributes.getNamedItem("r:embed");
								blipAttributesNode.setNodeValue(id);
							}
						}
					}
				}
			}
		}
	}
	
	private static int getPictureType(byte[] b) {
		if (b.length >= 8 && b[0] == (byte) 0x89 && b[1] == (byte) 0x50 && b[2] == (byte) 0x4E && b[3] == (byte) 0x47 && b[4] == (byte) 0x0D && b[5] == (byte) 0x0A && b[6] == (byte) 0x1A && b[7] == (byte) 0x0A) {
			return Document.PICTURE_TYPE_PNG;
		} else if (b.length >= 4 && b[0] == (byte) 0xff && b[1] == (byte) 0xd8 && b[2] == (byte) 0xff && b[3] == (byte) 0xd9) {
			return Document.PICTURE_TYPE_JPEG;
		} else if (b.length >= 4 && b[0] == (byte) 0xff && b[1] == (byte) 0xd8 && b[2] == (byte) 0xff && b[3] == (byte) 0xe0) {
			return Document.PICTURE_TYPE_JPEG;
		} else if (b.length >= 4 && b[0] == (byte) 0xff && b[1] == (byte) 0xd8 && b[2] == (byte) 0xff && b[3] == (byte) 0xe1) {
			return Document.PICTURE_TYPE_JPEG;
		} else if (b.length >= 4 && b[0] == (byte) 0xff && b[1] == (byte) 0xd8 && b[2] == (byte) 0xff && b[3] == (byte) 0xe8) {
			return Document.PICTURE_TYPE_JPEG;
		}else {
			String bStr = "";
			for (int i = 0; i < 8 && i < b.length; i++) {
				bStr += b[i] + ",";
			}
			if (b.length > 16) {
				bStr += "...,";
			}
			for (int i = Math.max(b.length - 8, 8); i < b.length; i++) {
				bStr += b[i] + ",";
			}
			throw new IllegalArgumentException("不支持的图片文件类型: " + bStr);
		}
	}
	
	private static String getText(XWPFParagraph paragraph) {
		StringBuilder sb = new StringBuilder();
		for (XWPFRun run: paragraph.getRuns()) {
			sb.append(getText(run));
		}
		return sb.toString();
		// return paragraph.getText(); 为兼容旧版POI调整方法
	}
	private static String getText(XWPFRun run) {
		// return run.text(); 为兼容旧版POI调整方法
		return run.toString();
	}
}
