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

import java.util.List;

import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings;
import com.knecon.fforesight.service.layoutparser.processor.model.table.Ruling;
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextDirection;
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;

import lombok.experimental.UtilityClass;

@UtilityClass
public class TextRulingsClassifier {

    private final static double STRIKETHROUGH_ZONE = 0.5; // multiplied with text height, determines height of intersection interval for strikethrough lines
    private final static double UNDERLINE_ZONE = 0.2; // multiplied with text height, determines height of intersection interval of underline lines
    private final static double TEXT_BBOX_THRESHOLD_FACTOR = 0.15; // multiplied with text width, subtracted from word width


    public static void classifyUnderlinedAndStrikethroughText(List<TextPositionSequence> words, CleanRulings cleanRulings) {

        for (TextPositionSequence word : words) {
            if (word.getDir().equals(TextDirection.ZERO) || word.getDir().equals(TextDirection.HALF_CIRCLE)) {
                handleHorizontalText(cleanRulings, word);
            } else {
                handleVerticalText(cleanRulings, word);
            }
        }
    }


    private static void handleVerticalText(CleanRulings cleanRulings, TextPositionSequence word) {

        float lowerY = (float) (word.getBoundingBox().getMinY() + TEXT_BBOX_THRESHOLD_FACTOR * word.getWidth());
        float upperY = (float) (word.getBoundingBox().getMaxY() - TEXT_BBOX_THRESHOLD_FACTOR * word.getWidth());

        float strikethroughCenterX = (float) word.getBoundingBox().getCenterX();
        float strikethroughBoxHeight = (float) ((word.getHeight() * STRIKETHROUGH_ZONE) / 2);

        float underlineCenterX = (float) (word.getDir().equals(TextDirection.QUARTER_CIRCLE) ? word.getBoundingBox().getMinX() : word.getBoundingBox().getMaxX());
        float underlineBoxHeight = (float) ((word.getHeight() * UNDERLINE_ZONE) / 2);

        float leftX = Math.min(underlineCenterX - underlineBoxHeight, strikethroughCenterX - strikethroughBoxHeight);
        float rightX = Math.max(underlineCenterX + underlineBoxHeight, strikethroughCenterX + strikethroughBoxHeight);

        List<Ruling> rulingsIntersectingWord = cleanRulings.getVerticalsInXInterval(leftX, rightX)
                .stream()
                .filter(ruling -> ruling.getClassification().equals(Ruling.Classification.OTHER))
                .filter(ruling -> ruling.y1 <= lowerY && upperY <= ruling.y2)
                .toList();

        for (Ruling ruling : rulingsIntersectingWord) {
            if (strikethroughCenterX - strikethroughBoxHeight < ruling.x1 && ruling.x1 < strikethroughCenterX + strikethroughBoxHeight) {
                ruling.setClassification(Ruling.Classification.STRIKETROUGH);
                word.setStrikethrough(true);
            }

            if (underlineCenterX - underlineBoxHeight < ruling.x1 && ruling.x1 < underlineCenterX + underlineBoxHeight) {
                ruling.setClassification(Ruling.Classification.UNDERLINE);
                word.setUnderline(true);
            }
        }
    }


    private static void handleHorizontalText(CleanRulings cleanRulings, TextPositionSequence word) {

        float leftX = (float) (word.getBoundingBox().getMinX() + TEXT_BBOX_THRESHOLD_FACTOR * word.getWidth());
        float rightX = (float) (word.getBoundingBox().getMaxX() - TEXT_BBOX_THRESHOLD_FACTOR * word.getWidth());

        float strikethroughCenterY = (float) word.getBoundingBox().getCenterY();
        float strikethroughBoxHeight = (float) ((word.getHeight() * STRIKETHROUGH_ZONE) / 2);

        float underlineCenterY = (float) (word.getDir().equals(TextDirection.ZERO) ? word.getBoundingBox().getMinY() : word.getBoundingBox().getMaxY());
        float underlineBoxHeight = (float) ((word.getHeight() * UNDERLINE_ZONE) / 2);

        float lowerY = Math.min(underlineCenterY - underlineBoxHeight, strikethroughCenterY - strikethroughBoxHeight);
        float upperY = Math.max(underlineCenterY + underlineBoxHeight, strikethroughCenterY + strikethroughBoxHeight);

        List<Ruling> rulingsIntersectingWord = cleanRulings.getHorizontalsInYInterval(lowerY, upperY)
                .stream()
                .filter(ruling -> ruling.getClassification().equals(Ruling.Classification.OTHER))
                .filter(ruling -> ruling.x1 <= leftX && rightX <= ruling.x2)
                .toList();

        for (Ruling ruling : rulingsIntersectingWord) {
            if (strikethroughCenterY - strikethroughBoxHeight < ruling.y1 && ruling.y1 < strikethroughCenterY + strikethroughBoxHeight) {
                ruling.setClassification(Ruling.Classification.STRIKETROUGH);
                word.setStrikethrough(true);
            }

            if (underlineCenterY - underlineBoxHeight < ruling.y1 && ruling.y1 < underlineCenterY + underlineBoxHeight) {
                ruling.setClassification(Ruling.Classification.UNDERLINE);
                word.setUnderline(true);
            }
        }
    }

}
