package incheon.ags.mrb.style.utils;

import static incheon.ags.mrb.style.utils.SldConstants.*;
import static incheon.ags.mrb.style.utils.SldElementBuilder.*;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import incheon.ags.mrb.style.vo.JsonStyleVO;

/**
 * Symbolizer 생성 팩토리 클래스
 * Point, Line, Polygon Symbolizer를 통합 생성
 */
public class SymbolizerFactory {

    /**
     * Point Symbolizer 생성 (통합)
     */
    public static void createPointSymbolizer(Document doc, Element rule, JsonStyleVO.SymbolConfig symbol, String iconUrl) {
        if (symbol == null) return;

        Element pointSymbolizer = createSld(doc, "PointSymbolizer").build();
        rule.appendChild(pointSymbolizer);

        Element graphic = createSld(doc, "Graphic").build();
        pointSymbolizer.appendChild(graphic);

        // 사용자 업로드 아이콘 (iconUrl이 전달된 경우)
        if ("userIcon".equals(symbol.getType()) && iconUrl != null && !iconUrl.isEmpty()) {
            createImageGraphic(doc, graphic, iconUrl, symbol.getIconId());
        }
        // 이미지 심볼 (기존 호환성)
        else if ("image".equals(symbol.getType()) && symbol.getImage() != null) {
            createImageGraphic(doc, graphic, symbol.getImage().getSrc(), null);
        }
        // 도형 심볼 (기본 아이콘: circle, square, triangle 등)
        else {
            createMarkGraphic(doc, graphic, symbol);
        }

        // Size
        if (symbol.getSize() != null) {
            graphic.appendChild(createSld(doc, "Size")
                    .text(String.valueOf(symbol.getSize()))
                    .build());
        }
    }

    /**
     * Line Symbolizer 생성 (통합)
     */
    public static void createLineSymbolizer(Document doc, Element rule, JsonStyleVO.StrokeConfig strokeConfig) {
        if (strokeConfig == null) return;

        Element lineSymbolizer = createSld(doc, "LineSymbolizer").build();
        rule.appendChild(lineSymbolizer);

        createStroke(doc, lineSymbolizer, strokeConfig);
    }

    /**
     * Polygon Symbolizer 생성 (통합)
     */
    public static void createPolygonSymbolizer(Document doc, Element rule,
                                                JsonStyleVO.SymbolConfig symbol,
                                                JsonStyleVO.FillConfig fillConfig) {
        Element polygonSymbolizer = createSld(doc, "PolygonSymbolizer").build();
        rule.appendChild(polygonSymbolizer);

        // Fill 처리
        if (fillConfig != null) {
            createFillFromConfig(doc, polygonSymbolizer, fillConfig);
        } else if (symbol != null) {
            createFillFromSymbol(doc, polygonSymbolizer, symbol);
        }

        // Stroke 처리
        if (symbol != null && symbol.getStroke() != null) {
            createStroke(doc, polygonSymbolizer, symbol.getStroke());
        } else if (symbol != null) {
            // Legacy 필드 지원
            createLegacyStroke(doc, polygonSymbolizer, symbol);
        }
    }

    /**
     * 이미지 그래픽 생성
     *
     * @param doc XML Document
     * @param parent 부모 Element (Graphic)
     * @param imageSrc 이미지 경로
     * @param iconId 아이콘 ID (userIcon 타입일 때 사용, null 가능)
     */
    private static void createImageGraphic(Document doc, Element parent, String imageSrc, String iconId) {
        Element externalGraphic = createSld(doc, "ExternalGraphic").build();
        parent.appendChild(externalGraphic);

        Element onlineResource = createSld(doc, "OnlineResource")
                .attr("xlink:type", "simple")
                .attr("xlink:href", imageSrc)
                .build();
        externalGraphic.appendChild(onlineResource);

        Element format = createSld(doc, "Format").text("image/png").build();
        externalGraphic.appendChild(format);

        // iconId가 있으면 VendorOption으로 저장
        if (iconId != null && !iconId.isEmpty()) {
            Element vendorOption = doc.createElement("VendorOption");
            vendorOption.setAttribute("name", "iconId");
            vendorOption.setTextContent(iconId);
            externalGraphic.appendChild(vendorOption);
        }
    }

    /**
     * 도형 그래픽 생성
     */
    private static void createMarkGraphic(Document doc, Element parent, JsonStyleVO.SymbolConfig symbol) {
        Element mark = createSld(doc, "Mark").build();
        parent.appendChild(mark);

        // WellKnownName
        Element wellKnownName = createSld(doc, "WellKnownName")
                .text(WellKnownName.get(symbol.getType()))
                .build();
        mark.appendChild(wellKnownName);

        // Fill with pattern support
        if (symbol.getFillPattern() != null && !"none".equals(symbol.getFillPattern())) {
            // GraphicFill을 사용한 패턴 채우기
            createGraphicFill(doc, mark, symbol);
        } else if (symbol.getColor() != null) {
            // 일반 단색 채우기
            SldElementBuilder fill = createSld(doc, "Fill")
                    .cssParam("fill", symbol.getColor());

            if (symbol.getOpacity() != null) {
                fill.cssParam("fill-opacity", normalizeOpacity(symbol.getOpacity()));
            }

            mark.appendChild(fill.build());
        }

        // Stroke
        if (symbol.getStroke() != null) {
            createStroke(doc, mark, symbol.getStroke());
        }
    }

    /**
     * GraphicFill을 사용한 패턴 채우기 생성
     */
    private static void createGraphicFill(Document doc, Element parent, JsonStyleVO.SymbolConfig symbol) {
        Element fill = createSld(doc, "Fill").build();
        parent.appendChild(fill);

        Element graphicFill = createSld(doc, "GraphicFill").build();
        fill.appendChild(graphicFill);

        Element graphic = createSld(doc, "Graphic").build();
        graphicFill.appendChild(graphic);

        Element mark = createSld(doc, "Mark").build();
        graphic.appendChild(mark);

        // 패턴 타입에 따른 WellKnownName 설정
        String patternShape = getPatternShape(symbol.getFillPattern());
        Element wellKnownName = createSld(doc, "WellKnownName")
                .text(patternShape)
                .build();
        mark.appendChild(wellKnownName);

        // 패턴 색상
        if (symbol.getColor() != null) {
            SldElementBuilder patternFill = createSld(doc, "Fill")
                    .cssParam("fill", symbol.getColor());
            
            if (symbol.getOpacity() != null) {
                patternFill.cssParam("fill-opacity", normalizeOpacity(symbol.getOpacity()));
            }
            
            mark.appendChild(patternFill.build());
        }

        // 패턴 크기 설정
        int patternSize = getPatternSize(symbol.getFillPattern());
        graphic.appendChild(createSld(doc, "Size").text(String.valueOf(patternSize)).build());
    }

    /**
     * fillPattern 타입을 SLD WellKnownName으로 변환
     */
    private static String getPatternShape(String fillPattern) {
        if (fillPattern == null) return "circle";
        
        switch (fillPattern.toLowerCase()) {
            case "stripe":
                return "shape://slash";  // 대각선 패턴
            case "dot":
                return "circle";  // 점 패턴
            case "grid":
                return "square";  // 격자 패턴
            default:
                return "circle";
        }
    }

    /**
     * fillPattern 타입에 따른 패턴 크기 반환
     */
    private static int getPatternSize(String fillPattern) {
        if (fillPattern == null) return 8;
        
        switch (fillPattern.toLowerCase()) {
            case "stripe":
                return 10;
            case "dot":
                return 4;
            case "grid":
                return 8;
            default:
                return 8;
        }
    }

    /**
     * Stroke 생성 (공통)
     */
    public static void createStroke(Document doc, Element parent, JsonStyleVO.StrokeConfig strokeConfig) {
        SldElementBuilder stroke = createSld(doc, "Stroke");

        if (strokeConfig.getColor() != null) {
            stroke.cssParam("stroke", strokeConfig.getColor());
        }

        if (strokeConfig.getWidth() != null) {
            stroke.cssParam("stroke-width", strokeConfig.getWidth());
        }

        if (strokeConfig.getOpacity() != null) {
            stroke.cssParam("stroke-opacity", normalizeOpacity(strokeConfig.getOpacity()));
        }

        // dashArray 처리
        if (strokeConfig.getDashArray() != null && strokeConfig.getDashArray().length > 0) {
            StringBuilder dashArrayStr = new StringBuilder();
            for (int i = 0; i < strokeConfig.getDashArray().length; i++) {
                if (i > 0) dashArrayStr.append(" ");
                dashArrayStr.append(strokeConfig.getDashArray()[i]);
            }
            stroke.cssParam("stroke-dasharray", dashArrayStr.toString());
        } else if (strokeConfig.getStyle() != null) {
            String dashArray = DashArrayStyle.get(strokeConfig.getStyle());
            if (dashArray != null) {
                stroke.cssParam("stroke-dasharray", dashArray);
            }
        }

        // lineCap 처리
        if (strokeConfig.getLineCap() != null) {
            stroke.cssParam("stroke-linecap", strokeConfig.getLineCap());
        } else if ("dotted".equals(strokeConfig.getStyle())) {
            stroke.cssParam("stroke-linecap", "round");
        }

        // lineJoin 처리
        if (strokeConfig.getLineJoin() != null) {
            stroke.cssParam("stroke-linejoin", strokeConfig.getLineJoin());
        }

        parent.appendChild(stroke.build());
    }

    /**
     * FillConfig로부터 Fill 생성
     */
    private static void createFillFromConfig(Document doc, Element parent, JsonStyleVO.FillConfig fillConfig) {
        SldElementBuilder fill = createSld(doc, "Fill");

        if (fillConfig.getColor() != null) {
            fill.cssParam("fill", fillConfig.getColor());
        }

        if (fillConfig.getOpacity() != null) {
            fill.cssParam("fill-opacity", normalizeOpacity(fillConfig.getOpacity()));
        }

        parent.appendChild(fill.build());
    }

    /**
     * SymbolConfig로부터 Fill 생성
     */
    private static void createFillFromSymbol(Document doc, Element parent, JsonStyleVO.SymbolConfig symbol) {
        // 패턴 채우기가 있는 경우
        if (symbol.getFillPattern() != null && !"none".equals(symbol.getFillPattern())) {
            createPolygonGraphicFill(doc, parent, symbol);
        } else {
            // 일반 단색 채우기
            SldElementBuilder fill = createSld(doc, "Fill");

            if (symbol.getColor() != null) {
                fill.cssParam("fill", symbol.getColor());
            }

            if (symbol.getOpacity() != null) {
                fill.cssParam("fill-opacity", normalizeOpacity(symbol.getOpacity()));
            }

            parent.appendChild(fill.build());
        }
    }

    /**
     * Polygon용 GraphicFill을 사용한 패턴 채우기 생성
     */
    private static void createPolygonGraphicFill(Document doc, Element parent, JsonStyleVO.SymbolConfig symbol) {
        Element fill = createSld(doc, "Fill").build();
        parent.appendChild(fill);

        Element graphicFill = createSld(doc, "GraphicFill").build();
        fill.appendChild(graphicFill);

        Element graphic = createSld(doc, "Graphic").build();
        graphicFill.appendChild(graphic);

        Element mark = createSld(doc, "Mark").build();
        graphic.appendChild(mark);

        // 패턴 타입에 따른 WellKnownName 설정
        String patternShape = getPatternShape(symbol.getFillPattern());
        Element wellKnownName = createSld(doc, "WellKnownName")
                .text(patternShape)
                .build();
        mark.appendChild(wellKnownName);

        // 패턴 색상
        if (symbol.getColor() != null) {
            SldElementBuilder patternFill = createSld(doc, "Fill")
                    .cssParam("fill", symbol.getColor());
            
            if (symbol.getOpacity() != null) {
                patternFill.cssParam("fill-opacity", normalizeOpacity(symbol.getOpacity()));
            }
            
            mark.appendChild(patternFill.build());
        }

        // 패턴 크기 설정
        int patternSize = getPatternSize(symbol.getFillPattern());
        graphic.appendChild(createSld(doc, "Size").text(String.valueOf(patternSize)).build());
    }

    /**
     * Legacy Stroke 생성 (하위 호환성)
     */
    private static void createLegacyStroke(Document doc, Element parent, JsonStyleVO.SymbolConfig symbol) {
        if (symbol.getStrokeColor() == null && symbol.getStrokeWidth() == null) {
            return;
        }

        SldElementBuilder stroke = createSld(doc, "Stroke");

        if (symbol.getStrokeColor() != null) {
            stroke.cssParam("stroke", symbol.getStrokeColor());
        }

        if (symbol.getStrokeWidth() != null) {
            stroke.cssParam("stroke-width", symbol.getStrokeWidth());
        }

        if (symbol.getStrokeOpacity() != null) {
            stroke.cssParam("stroke-opacity", normalizeOpacity(symbol.getStrokeOpacity()));
        }

        parent.appendChild(stroke.build());
    }
}
