package com.yaoyicloud.render;

import java.net.URL;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.imageio.ImageIO;

import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.poi.xwpf.usermodel.XWPFRun;

import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.data.ByteArrayPictureRenderData;
import com.deepoove.poi.data.HyperlinkTextRenderData;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.data.style.PictureStyle;
import com.deepoove.poi.exception.RenderException;
import com.deepoove.poi.policy.PictureRenderPolicy;
import com.deepoove.poi.policy.TextRenderPolicy;
import com.deepoove.poi.template.ElementTemplate;
import com.deepoove.poi.template.run.RunTemplate;
import com.deepoove.poi.xwpf.BodyContainer;
import com.deepoove.poi.xwpf.BodyContainerFactory;
import com.google.protobuf.Descriptors;
import com.google.protobuf.util.JsonFormat;
import com.yaoyicloud.message.FxyProtos.AttachmentSection;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.StrUtil;

/**
 * AttachmentSection渲染器
 *
 */
public final class AttachmentSectionRender extends AbstractRender {

    public AttachmentSectionRender(String cwd) {
        super(cwd);
    }

    /**
     * Docx 渲染
     *
     * @param info 数据
     * @param templateFileContent 模板内容
     * @return 本地文件目录
     * @throws IOException
     */
    public String renderDocx(AttachmentSection info, Map<String, Object> addtionalMap, byte[] templateFileContent)
        throws IOException {

        // 不需要定制展示逻辑的时候，使用protobuf的转json方法
        String jsonStr = JsonFormat.printer().print(info);

        // 定制缺省文字，POI-TL模板变量只支持if，不支持else
        Map<String, Object> newAddtionMap = new HashMap<>();
        Descriptors.Descriptor descriptor = info.getDescriptorForType();
        for (Descriptors.FieldDescriptor field : descriptor.getFields()) {
            if (field.isRepeated()) {
                List<?> li = (List<?>) info.getField(field);
                if (li.size() == 0) {
                    newAddtionMap.put(field.getName() + "AltText", "未上传");
                }
            }
        }
        newAddtionMap.putAll(addtionalMap);

        // 注： 报告模板的模板变量按照json序列化的结果命名
        // 注： 目前的实现假设：一个session对应一个cwd目录
        ConfigureBuilder builder = Configure.builder();

        // 新增特殊处理变量
        builder.addPlugin('^', new AttachmentRenderPolicy());

        this.docxResultPath = this.renderDocx(jsonStr, newAddtionMap, templateFileContent, builder,
            Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
        return this.docxResultPath;
    }

    /**
     * 这些render policy类都应当是共享的 重要设计假设: data的类型cast都可以建立在json通用反序列化后的基本类型基础上。
     */
    public class AttachmentRenderPolicy extends PictureRenderPolicy {

        public AttachmentRenderPolicy() {}

        @Override
        public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
            @SuppressWarnings("unchecked")
            Map<String, Object> mpData = (Map<String, Object>) data;
            String filename = mpData.getOrDefault("fileName", "").toString().trim();
            String url = mpData.getOrDefault("fileUri", "").toString();
            float targetWidth = 456.5f;

            if (StrUtil.isBlank(filename)) {
                // uri render when no filename
                TextRenderPolicy.Helper.renderTextRun(((RunTemplate) eleTemplate).getRun(),
                    new HyperlinkTextRenderData(url, url));
            } else if (filename.endsWith(".pdf")) {
                // pdf render, replace data with bytestream
                PDDocument document = null;
                try {
                    document = Loader.loadPDF(new URL(url).openStream().readAllBytes());
                    PDFRenderer renderer = new PDFRenderer(document);

                    XWPFRun run = ((RunTemplate) eleTemplate).getRun();
                    BodyContainer bodyContainer = BodyContainerFactory.getBodyContainer(run);
                    // 每页一张图片
                    for (int i = 0; i < document.getNumberOfPages(); i++) {
                        // pdf 转 jpeg
                        BufferedImage image = renderer.renderImageWithDPI(i, 150);
                        ByteArrayOutputStream stream = new ByteArrayOutputStream();
                        ImageIO.write(image, "jpg", stream);

                        // 准备docx元素
                        if (i == 0) {
                            run.setText("", 0);
                        } else {
                            run = bodyContainer.insertNewParagraph(run).createRun();
                        }

                        // 准备POI-TL数据
                        ByteArrayPictureRenderData picData =
                            new ByteArrayPictureRenderData(stream.toByteArray(), PictureType.JPEG);
                        Pair<Integer, Integer> targetSize =
                            getTargetSize(image.getWidth(), image.getHeight(), targetWidth);
                        PictureStyle style = new PictureStyle();
                        style.setWidth(targetSize.getKey());
                        style.setHeight(targetSize.getValue());
                        picData.setPictureStyle(style);
                        PictureRenderPolicy.Helper.renderPicture(run, picData);
                    }
                } catch (Exception e) {
                    throw new RenderException(
                        "AttachmentRenderPolicy for " + eleTemplate + " error: " + e.getMessage(), e);
                } finally {
                    if (document != null) {
                        try {
                            document.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } else {
                BufferedImage image = null;
                ByteArrayOutputStream stream = null;
                try {
                    image = ImageIO.read(new URL(url));
                    stream = new ByteArrayOutputStream();
                    ImageIO.write(image, filename.substring(filename.lastIndexOf('.') + 1, filename.length()), stream);
                } catch (IOException e) {
                    throw new RenderException(
                        "AttachmentRenderPolicy for " + eleTemplate + " error: " + e.getMessage(), e);
                }

                ByteArrayPictureRenderData picData =
                    new ByteArrayPictureRenderData(stream.toByteArray(), PictureType.suggestFileType(filename));
                Pair<Integer, Integer> targetSize = getTargetSize(image.getWidth(), image.getHeight(), targetWidth);
                PictureStyle style = new PictureStyle();
                style.setWidth(targetSize.getKey());
                style.setHeight(targetSize.getValue());
                picData.setPictureStyle(style);
                super.render(eleTemplate, picData, template);
            }
        }

        /**
         * 这个方法也应该独立出去共享
         *
         * @param originWidth
         * @param originHeight
         * @param targetWidth
         * @return
         */
        private static Pair<Integer, Integer> getTargetSize(int originWidth, int originHeight, float targetWidth) {
            float targetHeight = (originHeight * targetWidth) / originWidth;
            if (targetHeight > 645) {
                targetHeight = 645;
                targetWidth = (originWidth * targetHeight) / originHeight;
            }
            return Pair.of(Math.round(targetWidth), Math.round(targetHeight));
        }
    }
}
