package com.its.op.service.its.vms;

import com.its.op.dao.repository.its.vms.TbVmsDsplHsRepository;
import com.its.op.dto.its.vms.TbVmsDsplHsDto;
import com.its.op.dto.its.vms.TbVmsDsplHsImageDto;
import com.its.op.entity.its.vms.TbVmsCtlr;
import com.its.op.entity.its.vms.TbVmsDsplHs;
import com.its.op.entity.its.vms.TbVmsSymbLib;
import com.its.op.global.VmsSymbLibManager;
import com.its.utils.FloodFill;
import com.its.utils.ItsUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.sanselan.ImageReadException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@RequiredArgsConstructor
@Service
public class TbVmsDsplHsService {

    private final TbVmsDsplHsRepository repo;

    // 전체 데이터 조회
    @Transactional(readOnly = true)
    public List<TbVmsDsplHsDto> findAll() {
        List<TbVmsDsplHsDto> result = new ArrayList<>();
        List<TbVmsDsplHs> data = this.repo.findAll();
        for (TbVmsDsplHs entity : data) {
            result.add(entity.toDto());
        }
        return result;
    }

    /**
     * VMS 표출메시지 이력 조회
     * @param fromDt
     * @param toDt
     * @param ids
     * @return
     */
    public List<TbVmsDsplHsDto> findAllByDateRange(String fromDt, String toDt, List<Long> ids) {
        List<TbVmsDsplHsDto> result = new ArrayList<>();
        //log.error("SELECT");
        List<TbVmsDsplHs> data = this.repo.findAllByDateRange(fromDt, toDt, ids);
        //log.error("FETCH");
        if (data != null) {
            data.forEach(obj -> {
                result.add(obj.toDto());
            });
        }
        log.error("END");
        return result;
    }

    /**
     * VMS 표출메시지 이력 조회 - 메시지, 선택 VMS 의 표출시각에 대한 이력 메시지 조회
     * @param id
     * @param dsplDt
     * @return
     */
    public List<TbVmsDsplHsImageDto> findAllImageByDateRange(Long id, String dsplDt) {
        List<TbVmsDsplHsImageDto> result = new ArrayList<>();
        List<TbVmsDsplHs> data = this.repo.findAllImageByDateRange(id, dsplDt);
        if (data != null) {
            data.forEach(obj -> {
                TbVmsDsplHsImageDto dto = obj.toImageDto();
                dto.setImagFmt("png");
                try {
                    dto.setImagData(createVmsDsplImage(dto.getImagFmt(), obj.getVms(), id, obj.getVmsDsplMsgData(), obj.getPhase()));
                    result.add(dto);
                } catch (IOException | ImageReadException e) {
                    log.error("{}", e.getMessage());
                }
            });
        }
        return result;
    }

    /**
     * VMS 이미지를 그린다
     * @param imagFmt
     * @param obj
     * @param id
     * @param msgData
     * @param phaseNum
     * @return
     * @throws IOException
     * @throws ImageReadException
     */
    public byte[] createVmsDsplImage(String imagFmt, TbVmsCtlr obj, Long id, String msgData, int phaseNum) throws IOException, ImageReadException {

        String saveDir = ItsUtils.createUserDir("/image/vms/dspl-hs/");
        if (obj == null || obj.getType() == null) {
            log.error("Not Found VMS Controller: {}, {}", id, obj);
            return null;
        }

        //int formKind  = Integer.parseInt(msgData.substring(0, 2), 16);
        //int dispTime  = Integer.parseInt(msgData.substring(2, 4), 16);
        int formColor = Integer.parseInt(msgData.substring(4, 6), 16);

        /**
         * 배경 이미지를 만든다.
         */
        int width = obj.getType().getVmsWdth();
        int height = obj.getType().getVmsHght();
        // TYPE_INT_ARGB specifies the image format: 8-bit RGBA packed
        BufferedImage formImage = new BufferedImage(width, height, (("png").equals(imagFmt) ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB));
        Graphics2D 	g2d = (Graphics2D)formImage.createGraphics();  //createGraphics();getGraphics();
        //g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(getColor(formColor));
        g2d.fillRect(0, 0, width, height);

        List<String> msgList = ItsUtils.split(msgData, "|");
        if (msgList.size() > 1) {
            for (int ii = 1; ii < msgList.size(); ii++) {
                String tmp = msgList.get(ii);
                int objType = Integer.parseInt(tmp.substring(0, 2), 16);
                //int objLen  = Integer.parseInt(tmp.substring(2, 4), 16);

                //int blank   = Integer.parseInt(tmp.substring( 4, 6), 16);
                int left    = Integer.parseInt(tmp.substring( 6, 10), 16);
                int top     = Integer.parseInt(tmp.substring(10, 14), 16);
                //int bkColor = Integer.parseInt(tmp.substring(14, 16), 16);

                if (objType == 1) { // text
                    int fontSize  = Integer.parseInt(tmp.substring(16, 18), 16);
                    int fontColor = Integer.parseInt(tmp.substring(18, 20), 16);
                    int fontName  = Integer.parseInt(tmp.substring(20, 22), 16);
                    int fontBold  = Integer.parseInt(tmp.substring(22, 24), 16);
                    //int textSize  = Integer.parseInt(tmp.substring(24, 26), 16);
                    String text   = tmp.substring(26, tmp.length());

                    /**
                     * 문자를 이미지에 그린다.
                     * 1.35f : 텍스트상자 패딩값
                     */
                    //g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                    //g2d.setFont(g2d.getFont().deriveFont(g2d.getFont().getSize() * 1.35f));
                    int fontStyle = (fontBold == 1) ? Font.BOLD : Font.PLAIN;
                    Font txtFont = new Font(getFontName(fontName), fontStyle, fontSize);
                    txtFont = txtFont.deriveFont(fontSize * 1.35f);
                    g2d.setFont(txtFont);
                    FontMetrics fm = g2d.getFontMetrics(txtFont);
                    g2d.setColor(getColor(fontColor));
                    g2d.drawString(text, left, top + fm.getAscent());//fm.getHeight() - fm.getDescent() - fm.getLeading());
                    //g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                    //g2d.drawString(text, left, top + fontSize);
                }
                else if (objType == 2) {    // symbol id
                    String symbLibId = tmp.substring(16, tmp.length());
                    /**
                     * 심볼을 이미지 위에 그린다.
                     */
                    TbVmsSymbLib symbLib = VmsSymbLibManager.get(Integer.parseInt(symbLibId));
                    if (symbLib != null) {
                        //BufferedImage symbol = Sanselan.getBufferedImage(symbLib.getImagData());
                        //g2d.drawImage(symbol, left, top, null);
                        Image imgBmp = symbLib.getImage();
                        if (imgBmp != null) {
                            //g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                            g2d.drawImage(imgBmp, left, top, imgBmp.getWidth(null), imgBmp.getHeight(null), null);
                        }
                        else {
                            log.error("symbol image null id: {}, {}, {}", id, phaseNum, symbLibId);
                        }
                    }
                    else {
                        log.error("Not found symbol id: {}, {}, {}", id, phaseNum, symbLibId);
                    }
                }
                else if (objType == 3) { // draw traffic
                    List<String> trafList = ItsUtils.split(tmp, "+");
                    if (trafList.size() == 2) {
                        tmp = trafList.get(0);
                        String symbLibId = tmp.substring(16, tmp.length());
                        TbVmsSymbLib symbLib = VmsSymbLibManager.get(Integer.parseInt(symbLibId));
                        if (symbLib != null) {
                            Image imgBmp = symbLib.getImage();
                            //g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                            if (imgBmp != null) {
                                BufferedImage trafImg = (BufferedImage) imgBmp;
                                FloodFill floodFill = new FloodFill(imgBmp);
                                floodFill.setAntialiased(false);

                                tmp = trafList.get(1);
                                if (!("X").equals(tmp)) {
                                    int vertex = Integer.parseInt(tmp.substring(0, 2), 16);
                                    for (int jj = 0; jj < vertex; jj++) {
                                        int idx = jj * 7;
                                        int cellX = Integer.parseInt(tmp.substring(idx + 2, idx + 2 + 3));
                                        int cellY = Integer.parseInt(tmp.substring(idx + 5, idx + 5 + 3));
                                        int gradF = Integer.parseInt(tmp.substring(idx + 8, idx + 8 + 1));
                                        /**
                                         * 소통정보 색상 표출 심볼을 이미지 위에 그린다.
                                         */
                                        int orgColor = trafImg.getRGB(cellX, cellY);
                                        int trfColor = getGradColor(gradF);
                                        if (orgColor != trfColor) {
                                            floodFill.fill(cellX, cellY, getTrafColor(gradF));
                                        }
                                    }
                                    //g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                                    g2d.drawImage(floodFill.getImage(), left, top, imgBmp.getWidth(null), imgBmp.getHeight(null), null);
                                }
                            }
                            else {
                                log.error("symbol image null id: {}, {}, {}", id, phaseNum, symbLibId);
                            }
                        }
                        else {
                            log.error("Not found symbol id: {}, {}, {}", id, phaseNum, symbLibId);
                        }
                    }
                }
            }
        }

        //ItsUtils.saveImageFile(formImage, imagFmt, saveDir + obj.getVmsCtlrNmbr() + "_" + phaseNum + "." + imagFmt);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write(formImage, imagFmt, out);
        g2d.dispose();
        return out.toByteArray();
    }

    private int getCodeColor(int code) {
        if (code == 0) return Color.BLACK.getRGB();
        if (code == 1) return Color.RED.getRGB();
        if (code == 2) return Color.GREEN.getRGB();
        if (code == 3) return Color.YELLOW.getRGB();
        return Color.BLACK.getRGB();
    }
    private Color getColor(int code) {
        if (code == 0) return Color.BLACK;
        if (code == 1) return Color.RED;
        if (code == 2) return Color.GREEN;
        if (code == 3) return Color.YELLOW;
        return Color.BLACK;
    }

    private String getFontName(int fontCode) {
        switch(fontCode)
        {
            case 1: return "맑은 고딕";
            case 2: return "HY견명조";
            case 3: return "굴림체";
            case 4: return "궁서체";
            case 5: return "돋움체";
            case 6: return "바탕체";
        }
        return "맑은 고딕";
    }
    private int getGradColor(int grad) {
        if (grad == 1) return Color.GREEN.getRGB();
        if (grad == 2) return Color.YELLOW.getRGB();
        if (grad == 3) return Color.RED.getRGB();
        return Color.GRAY.getRGB();
    }

    private Color getTrafColor(int grad) {
        if (grad == 1) return Color.GREEN;
        if (grad == 2) return Color.YELLOW;
        if (grad == 3) return Color.RED;
        return Color.GRAY;
    }

}
