package com.nantian.cloud.jljc.tools;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.rmi.RemoteException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
public class EditWordZZUtil {


    /**
     * 对word进行参数替换并生成新的文件
     *
     * @param
     * @param map 待填充的数据 map.put("key",value) 文档中对应为 ${key}
     * @throws Exception
     */
    public static void readWriteWord(XWPFDocument document, Map<String, Object> map) throws Exception {
        replaceParams(document, map);
        replaceTableParams(document, map);
    }

    /**
     * 替换word中的参数与动态新增表格行数
     *
     * @param params     待填充的数据 params.put("key",value) 文档中对应为 ${key}
     * @param tableIndex 表格下标，从0开始，如Word中3个表格，仅对1、3个表格进行修改，参数为 int[]{0,2}
     * @param tables     新增表格的数据，数据格式为 [表[行[单元格]]]
     * @throws Exception
     */
    public static XWPFDocument readWriteWord(XWPFDocument document, Map<String, Object> params, int[] tableIndex, List<List<String[]>> tables) throws Exception {
        if (tableIndex.length != tables.size()) throw new RemoteException("表格下标数量与表格参数数量不一致！");
        for (int i = 0; i < tableIndex.length; i++) {
            changeTable(document, params, i, tables.get(i));
        }
        return document;
    }

    /**
     * 实现对word读取和修改操作
     *
     * @param in     模板输入流
     * @param out    输出流
     * @param params 待填充的数据，从数据库读取
     */
    public static void readWriteWord(InputStream in, OutputStream out, Map<String, Object> params) {

        try {
            XWPFDocument document = new XWPFDocument(OPCPackage.open(in));
            replaceParams(document, params);
            replaceTableParams(document, params);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InvalidFormatException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param document   docx解析对象
     * @param params     需要替换的信息集合
     * @param tableList  第几个表格
     * @param tableIndex 需要插入的表格信息集合
     */
    public static void changeTable(XWPFDocument document, Map<String, Object> params, int tableIndex, List<String[]> tableList) {

        //获取表格对象集合
        List<XWPFTable> tables = document.getTables();
        //获取第一个表格，根据实际模板情况 决定去第几个word中的表格
        XWPFTable table = tables.get(tableIndex);
        //替换表格中的参数
//        replaceParams(document, params);
        //在表格中插入数据
        insertTable(table, tableList);
    }

    /**
     * 替换段落里面的变量
     *
     * @param document 要替换的文档
     * @param map      参数
     */
    public static void replaceParams(XWPFDocument document, Map<String, Object> map) {

        Iterator<XWPFParagraph> paragraphsIterator = document.getParagraphsIterator();
        XWPFParagraph paragraph = null;
        while (paragraphsIterator.hasNext()) {
            paragraph = paragraphsIterator.next();
            replaceParam(paragraph, map);
        }
    }


    /**
     * 删除word中的空白行
     * @param document
     */
    public static void changeText(XWPFDocument document){
        //获取文字段落集合
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        //所有类型集合（文字段落、表格、图片等）
        List<IBodyElement> listBe = document.getBodyElements();
        List<Integer> runList = new ArrayList<>();
        int n = 0;

        for(int i = 0; i < listBe.size(); i++){

            //BodyElementType.PARAGRAPH ： 枚举中的文字段落
            //文字为空时，先添加到list中；
            //注意picture类型也在PARAGRAPH中，需要校验embeddedPictures的长度是否为0
            //为0表示空行，大于0表示有图片，可能还有其他类型，暂时没遇到，各位自行斟酌
            if (paragraphs.get(n).getRuns().size() > 0){
                if(StringUtils.isEmpty(paragraphs.get(n).getRuns().get(0).text())
                        && paragraphs.get(n).getRuns().get(0).getEmbeddedPictures().size() == 0){
                    runList.add(i);

                }

                n++;

                //非文字段落n-1
                if(listBe.get(i).getElementType() != BodyElementType.PARAGRAPH){
                    n--;
                }
            }
        }

        //遍历list删除
        for(int i = runList.size() - 1; i >= 0; i--){
            document.removeBodyElement(runList.get(i));
        }
    }

    /**
     * 替换表格里面的变量
     *
     * @param document 要替换的文档
     * @param map      参数
     */
    public static void replaceTableParams(XWPFDocument document, Map<String, Object> map) {
        Iterator<XWPFTable> tablesIterator = document.getTablesIterator();
        XWPFTable table;
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        List<XWPFParagraph> paragraphs;
        while (tablesIterator.hasNext()) {
            table = tablesIterator.next();
            //判断表格是需要替换还是需要插入，判断逻辑有$为替换，表格无$为插入
            if (matcher(table.getText()).find()) {
                rows = table.getRows();
                for (XWPFTableRow row : rows) {
                    cells = row.getTableCells();
                    for (XWPFTableCell cell : cells) {
                        paragraphs = cell.getParagraphs();
                        for (XWPFParagraph paragraph : paragraphs) {
                            replaceParam(paragraph, map);
                        }
                    }
                }
            }
        }
    }

    /**
     * 替换段落里面的变量
     *
     * @param paragraph 要替换的段落
     * @param params    参数
     */
    private static void replaceParam(XWPFParagraph paragraph, Map<String, Object> params) {
        List<XWPFRun> runs;
        Matcher matcher;
        String runText = "";

        if (matcher(paragraph.getParagraphText()).find()) {
            runs = paragraph.getRuns();
            int j = runs.size();
            for (int i = 0; i < j; i++) {
                runText += runs.get(0).toString();
                //保留最后一个段落，在这段落中替换值，保留段落样式
                if (!((j - 1) == i)) {
                    paragraph.removeRun(0);
                }
            }
            matcher = matcher(runText);
            if (matcher.find()) {
                while ((matcher = matcher(runText)).find()) {
                    runText = matcher.replaceFirst(String.valueOf(params.get(matcher.group(1))));
                }
                runs.get(0).setText(runText, 0);
            }
        }
    }

    /**
     * 替换List中的内容
     * @param tableList
     * @return
     */
    public static List<String[]> replaceStr(List<String[]> tableList){
        List<String[]> list = new ArrayList<>();
        list.add(tableList.get(0));
        int flag = 0; //判断单位
        if (Arrays.asList(tableList.get(0)).contains("符号")){
            int count = 0; //判断符号
            for (int j = 0; j < tableList.get(0).length; j++) {
                if (tableList.get(0)[j].equals("符号")){
                    count = j;
                }
                if (tableList.get(0)[j].equals("单位")){
                    flag = j + 1;
                }
            }
            for (int i = 1; i < tableList.size(); i++) {
                //删除、替换String[]中的元素
                List<String> stringList = new ArrayList<String>(Arrays.asList(tableList.get(i)));
                if (!isChinese(stringList.get(count))){
                    stringList.set(count, "");
                }
                if (!isChinese(stringList.get(flag))){
                    stringList.set(flag, "");
                }
                for (int j = stringList.size() - 1; j >= 1; j--) {
                    if (j == count + 1){
                        stringList.remove(j);
                    }
                    if (j == flag + 1){
                        stringList.remove(j);
                    }
                }
                String[] strings = stringList.toArray(new String[stringList.size()]);
                list.add(strings);
            }
        }else {
            for (int j = 0; j < tableList.get(0).length; j++) {
                if (tableList.get(0)[j].equals("单位")){
                    flag = j;
                }
            }
            for (int i = 1; i < tableList.size(); i++) {
                //删除、替换String[]中的元素
                List<String> stringList = new ArrayList<String>(Arrays.asList(tableList.get(i)));
                if (!isChinese(stringList.get(flag))){
                    stringList.set(flag, "");
                }
                for (int j = stringList.size() - 1; j >= 1; j--) {
                    if (j == flag + 1){
                        stringList.remove(j);
                    }
                }
                String[] strings = stringList.toArray(new String[stringList.size()]);
                list.add(strings);
            }
        }

        return list;
    }

    /**
     * 获取单位和符号在数组中的位置
     * @param tableList
     * @return
     */
    public static int[] getListUnit(List<String[]> tableList){
        int[] i = {};
        if (Arrays.asList(tableList.get(0)).contains("符号")){
            int count = 0;
            int flag = 0;
            for (int j = 0; j < tableList.get(0).length; j++) {
                if (tableList.get(0)[j].equals("符号")){
                    count = j;
                }
                if (tableList.get(0)[j].equals("单位")){
                    flag = j;
                }
            }
            i = new int[]{count, flag};
        }else {
            int flag = 0;
            for (int j = 0; j < tableList.get(0).length; j++) {
                if (tableList.get(0)[j].equals("单位")){
                    flag = j;
                }
            }
            i = new int[]{flag};
        }
        return i;
    }

    /**
     * 获取符号和单位中的数据
     * @param tableList
     * @return
     */
    public static List<String[]> getListInfo(List<String[]> tableList){
        List<String[]> list = new ArrayList<>();
        int count = 0; //判断符号
        int flag = 0; //判断单位
        String[] strings1 = tableList.get(0);
        if (Arrays.asList(strings1).contains("符号")){
            for (int j = 0; j < tableList.get(0).length; j++) {
                if (tableList.get(0)[j].equals("符号")){
                    count = j;
                }
                if (tableList.get(0)[j].equals("单位")){
                    flag = j + 1;
                }
            }
            for (int i = 1; i < tableList.size(); i++) {
                String[] strings = tableList.get(i);
                String[] str = {strings[count],strings[count + 1],strings[flag],strings[flag + 1]};
                list.add(str);
            }
        }else {
            for (int j = 0; j < tableList.get(0).length; j++) {
                if (tableList.get(0)[j].equals("单位")){
                    flag = j + 1;
                }
            }
            for (int i = 1; i < tableList.size(); i++) {
                String[] strings = tableList.get(i);
                String[] str = {strings[flag - 1],strings[flag]};
                list.add(str);
            }
        }
        return list;
    }

    /**
     * 为表格插入行数，此处不处理表头，所以从第二行开始
     *
     * @param table     需要插入数据的表格
     * @param tableList 插入数据集合
     */
    public static void insertTable(XWPFTable table, List<String[]> tableList) {
        boolean is = true;
        List<Integer> count = new ArrayList<>();
        //创建与数据一致的行数
        for (int i = 0; i < tableList.size(); i++) {
            table.createRow();
            for (int j = 0; j < tableList.get(i).length; j++) {
                is = is && isChinese(tableList.get(i)[j]);
            }
            if (is == true){
                count.add(i + 1);
            }
            is = true;
        }
        int size = table.getRows().size();
        for (int i = 1; i < size; i++) {
            XWPFTableRow newRow = table.getRow(i);
            List<XWPFTableCell> cells = newRow.getTableCells();
            for (int j = 0; j < cells.size(); j++) {
                if (!count.contains(i)){
                    XWPFTableCell cell = cells.get(j);
                    XWPFRun run = cell.addParagraph().createRun();
                    run.setText(tableList.get(i - 1)[j]);
                    cell.removeParagraph(0);
                    CTTc ctTc = cell.getCTTc();
                    CTTcPr ctTcPr = ctTc.addNewTcPr();
                    ctTcPr.addNewVAlign().setVal(STVerticalJc.CENTER);
                    ctTc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.CENTER );
                }else {
                    XWPFTableCell cell = cells.get(j);
                    XWPFRun run = cell.addParagraph().createRun();
                    run.setText(tableList.get(i - 1)[j]);
                    run.setBold(true);
                    cell.removeParagraph(0);
                    CTTc ctTc = cell.getCTTc();
                    CTTcPr ctTcPr = ctTc.addNewTcPr();
                    ctTcPr.addNewVAlign().setVal(STVerticalJc.CENTER);
                    ctTc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.CENTER );
                }

            }
        }
    }

    public static boolean isChinese(String strName) {
        if (StringUtils.isNotBlank(strName)){
            char[] ch = strName.toCharArray();
            for (int i = 0; i < ch.length; i++) {
                char c = ch[i];
                if (isChinese(c)) {
                    return true;
                }
            }
            return false;
        }
        return false;
    }

    public static boolean isChinese(char c) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
        if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION) {
            return true;
        }
        return false;
    }

    /**
     * word 跨列合并单元格
     * @param table
     * @param row
     * @param fromCell
     * @param toCell
     */
    public static void mergeCellsHorizontal(XWPFTable table, int row, int fromCell, int toCell) {
        for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {
            XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
            if ( cellIndex == fromCell ) {
                // The first merged cell is set with RESTART merge value
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
            } else {
                // Cells which join (merge) the first one, are set with CONTINUE
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
            }
        }
    }

    /**
     * 正则匹配字符串
     *
     * @param str
     * @return
     */
    private static Matcher matcher(String str) {
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }

    /**
     * 设置word页边距 计算方法为（磅 * 20）例如：页边距1cm，1 * 28.35 * 20
     *
     * @param document docx对象
     * @param left
     * @param right
     * @param top
     * @param bottom
     */
    /*public static void setPageMargin(XWPFDocument document, long left, long right, long top, long bottom) {

        CTSectPr ctSectPr = document.getDocument().getBody().addNewSectPr();
        CTPageMar pageMar = ctSectPr.addNewPgMar();
        pageMar.setLeft(BigInteger.valueOf(left));
        pageMar.setRight(BigInteger.valueOf(right));
        pageMar.setTop(BigInteger.valueOf(top));
        pageMar.setBottom(BigInteger.valueOf(bottom));
    }*/

    /**
     * 设置字体样式，对document中的run对象操作
     *
     * @param run         一个XWPFRun代表具有相同属性的一个区域
     * @param fontFamily  字体
     * @param fontSize    大小（磅，例如10）
     * @param fontNum     大小（字号，例如小四）
     * @param isBold      是否加粗
     * @param isItalic    是否斜体
     * @param isThrough   是否有删除线
     * @param isUnderLine 是否有下划线
     * @param color       颜色 十六进制，如FFFFFF
     * @param bgColor     背景色 十六进制，如FFFFFF
     */
    public static XWPFRun setFontStyle(XWPFRun run, String text, String fontFamily, Integer fontSize, long fontNum,
                                       boolean isBold, boolean isItalic, boolean isThrough, boolean isUnderLine, String color,
                                       String bgColor) {
        if (run.getText(0) == text || run.getText(0).equals(text)) {
            run.setFontFamily(fontFamily);
            if (fontSize == null || "".equals(fontSize)) {
                CTRPr ctrPr = run.getCTR().addNewRPr();
                /**
                 * 传入的值是磅数*2,
                 * 例如想将字号设置为五号字，五号字对应的磅数是10.5，则传入的值就是 10.5 * 2 = 21
                 */
                ctrPr.addNewSz().setVal(BigInteger.valueOf(fontNum));
                ctrPr.addNewSzCs().setVal(BigInteger.valueOf(fontNum));
            } else {
                run.setFontSize(fontSize);
            }
            if (isBold) {
                run.setBold(true);
            }
            if (isItalic) {
                run.setItalic(true);
            }
            if (isThrough) {
                run.setStrikeThrough(true);
            }
            if (isUnderLine) {
                run.setUnderline(UnderlinePatterns.SINGLE);
            }
            if (color != null || StringUtils.isNotEmpty(color)) {
                run.setColor(color);
            }
            if (bgColor != null || StringUtils.isNotEmpty(bgColor)) {
                run.getCTR().addNewRPr().addNewShd().setFill(bgColor);
            }
            return run;
        } else {
            return null;
        }
    }

    /**
     * 设置word中表格内容水平和垂直居中。
     *
     * @param cell
     */
    public static void setCellText(XWPFTableCell cell) {
        CTTc cttc = cell.getCTTc();
        CTTcPr ctPr = cttc.addNewTcPr();
        CTShd ctshd = ctPr.addNewShd();
        // 垂直居中
        ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
        /*
         * 水平居中 下面的方法需要引入ooxml-schemas-1.X.jar包而不是poi-ooxml-schemas-XX.jar
         * 下载地址：https://repo1.maven.org/maven2/org/apache/poi/ooxml-schemas/1.3/
         */
//		List<CTP> listCTP = cttc.getPList();
//		CTP ctp = listCTP.get(0);
//		CTPPr cr = ctp.addNewPPr();
//		CTJc cc = cr.addNewJc();
//		cc.setVal(STJc.CENTER);
        cttc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.CENTER);

    }


    /**
     * 给${total}、${index}设置下划线
     * @param document
     * @param map
     */
    public static void setUnderLine(XWPFDocument document, Map<String, Object> map) {
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        for (XWPFParagraph paragraph : paragraphs) {
            for (XWPFRun run : paragraph.getRuns()) {
                if (run.getText(0).equals("${total}")) {
                    String total = String.valueOf(map.get("total"));
                    run.setText(total, 0);
                    run.setUnderline(UnderlinePatterns.SINGLE);
                }
                if (run.getText(0).equals("${index}")) {
                    String index = String.valueOf(map.get("index"));
                    run.setText(index, 0);
                    run.setUnderline(UnderlinePatterns.SINGLE);
                }
            }
        }
    }

    /**
     * 给${total}、${index}设置下划线
     * @param document
     * @param map
     */
    public static void setUnderLine(XWPFDocument document, Map<String, Object> map, int i) {
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        for (XWPFParagraph paragraph : paragraphs) {
            for (XWPFRun run : paragraph.getRuns()) {
                if (run.getText(0) != null){
                    if (run.getText(0).equals("${total}")) {
                        String total = String.valueOf(map.get("total"));
                        run.setText(total, 0);
                        run.setUnderline(UnderlinePatterns.SINGLE);
                    }
                    if (run.getText(0).equals("${index}")) {
                        String index = String.valueOf(map.get("index"));
                        run.setText(index, 0);
                        run.setUnderline(UnderlinePatterns.SINGLE);
                    }
                }
            }
        }
    }

    /**
     * 设置“签名或盖章：”字体加粗
     * @param document
     * @return
     */
    public static void setBold(XWPFDocument document,String str){
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        for (XWPFParagraph paragraph : paragraphs) {
            List<XWPFRun> runs = paragraph.getRuns();
            for (int i = 0; i < runs.size(); i++) {
                if (runs.get(i).getText(0) != null){
                    if (runs.get(i).getText(0).equals(str)){
                        runs.get(i).setBold(true);
                        runs.get(i + 1).setBold(true);
                    }
                }
            }
        }
    }

    /**
     * 去除 list.toString()中的前后[]
     * @param str
     * @return
     */
    public static String replace(String str) {
        str = str.substring(1, str.length() - 1);
        return str;
    }

    /**
     * 去除 list.toString()中的前后[]，并替换","为”、“
     * @param list
     * @return
     */
    public static String replace(List<String> list) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < list.size(); i++) {
            stringBuilder.append(list.get(i)).append("、");
        }
        stringBuilder.deleteCharAt(stringBuilder.length() - 1);
        String str = stringBuilder.toString();
        return str;
    }

    /**
     * 关闭输入流
     */
    public static void closeInputStream(InputStream in) {
        if (in != null) {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void closeOutputStream(OutputStream ot) {
        if (ot != null) {
            try {
                ot.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
