package com.knecon.fforesight.service.layoutparser.processor.utils;

import java.awt.geom.Rectangle2D;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDMarkedContent;
import org.apache.pdfbox.text.TextPosition;

import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageBlock;
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;

import lombok.experimental.UtilityClass;

@UtilityClass
public class MarkedContentUtils {

    public static final String HEADER = "Header";
    public static final String FOOTER = "Footer";


    public List<Rectangle2D> getMarkedContentBboxPerLine(List<PDMarkedContent> markedContents, String subtype, PDPage pdPage) {

        if (markedContents == null) {
            return Collections.emptyList();
        }

        var markedContentByYPosition = markedContents.stream()
                .filter(m -> m.getTag().equals("Artifact"))
                .filter(m -> m.getProperties() != null)
                .filter(m -> m.getProperties().getItem("Subtype") != null)
                .filter(m -> ((COSName) m.getProperties().getItem("Subtype")).getName().equals(subtype))
                .map(PDMarkedContent::getContents)
                .flatMap(Collection::stream)
                .filter(t -> t instanceof TextPosition)
                .map(t -> (TextPosition) t)
                .filter(t -> !t.getUnicode().equals(" "))
                .collect(Collectors.groupingBy(TextPosition::getY));

        if (markedContentByYPosition.isEmpty()) {
            return Collections.emptyList();
        }

        return markedContentByYPosition.values()
                .stream()
                .map(textPositions -> new TextPositionSequence(textPositions, 0, true).getRectangle())
                .map(t -> new Rectangle2D.Float(t.getTopLeft().getX(), t.getTopLeft().getY() - Math.abs(t.getHeight()), t.getWidth(), Math.abs(t.getHeight())))
                .collect(Collectors.toList());
    }


    public List<MarkedContentPosition> getMarkedContentPositions(List<PDMarkedContent> markedContents, PDPage pdPage) {

        if (markedContents == null) {
            return Collections.emptyList();
        }

        return markedContents.stream()
                .filter(m -> !m.getContents().isEmpty())
                .map(markedContent -> MarkedContentPosition.fromPDMarkedContent(markedContent, pdPage))
                .toList();
    }


    public boolean intersects(TextPageBlock textBlock, Map<String, List<Rectangle2D>> markedContentBboxPerType, String type) {

        return markedContentBboxPerType.get(type) != null && markedContentBboxPerType.get(type)
                .stream()
                .anyMatch(rectangle -> rectangle.intersects(textBlock.getPdfMinX(), textBlock.getPdfMinY(), textBlock.getWidth(), textBlock.getHeight()));
    }


    public record MarkedContentPosition(String type, String subType, List<Rectangle2D> textPositions) {

        public static MarkedContentPosition fromPDMarkedContent(PDMarkedContent markedContent, PDPage pdPage) {

            return new MarkedContentPosition(markedContent.getTag(), parseSubType(markedContent), parseTextPositions(markedContent.getContents(), pdPage));
        }


        private static List<Rectangle2D> parseTextPositions(List<Object> contents, PDPage pdPage) {

            return contents.stream()
                    .filter(content -> content instanceof TextPosition)
                    .map(content -> (TextPosition) content)
                    .filter(content -> !content.getUnicode().equals(" "))
                    .map(textPositions -> new TextPositionSequence(List.of(textPositions), 0, true).getRectangle())
                    .map(t -> new Rectangle2D.Float(t.getTopLeft().getX(), t.getTopLeft().getY() - Math.abs(t.getHeight()), t.getWidth(), Math.abs(t.getHeight())))
                    .collect(Collectors.toList());
        }


        private static String parseSubType(PDMarkedContent markedContent) {

            if (markedContent == null || markedContent.getProperties() == null || markedContent.getProperties().getItem("Subtype") == null) {
                return null;
            }

            return ((COSName) markedContent.getProperties().getItem("Subtype")).getName();
        }


        public String formattedType() {

            if (subType == null || subType.isEmpty()) {
                return type;
            }
            if (type.equals("Artifact")) {
                return subType;
            }
            return String.format("%s-%s", type, subType);

        }

    }

}
