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

import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Stream;

import com.knecon.fforesight.service.layoutparser.processor.model.GapInformation;
import com.knecon.fforesight.service.layoutparser.processor.model.PageContents;

import lombok.experimental.UtilityClass;

@UtilityClass
public class DividingColumnDetectionService {

    private static final int MAX_NUMBER_OF_COLUMNS = 200;

    private static final int LINE_COUNT_THRESHOLD = 5;


    public List<Rectangle2D> detectColumns(PageContents pageContents) {


        if (pageContents.getSortedTextPositionSequences().size() < 2) {
            return List.of(pageContents.getCropBox());
        }

        GapInformation linesWithGapInformation = GapDetectionService.findGapsInLines(pageContents.getSortedTextPositionSequences(), pageContents.getCropBox());

        return detectColumnsFromLines(linesWithGapInformation.getXGaps(), pageContents.getCropBox());
    }


    public List<Rectangle2D> detectColumnsFromLines(List<List<Rectangle2D>> gaps, Rectangle2D mainBodyTextFrame) {

        List<List<Line2D>> allColumnParts = new ArrayList<>(MAX_NUMBER_OF_COLUMNS);
        for (int columnIndex = 1; columnIndex < MAX_NUMBER_OF_COLUMNS; columnIndex++) {
            double x = calculateGapLocation(columnIndex, MAX_NUMBER_OF_COLUMNS, mainBodyTextFrame.getWidth()) + mainBodyTextFrame.getMinX();
            double currentMinY = mainBodyTextFrame.getMaxY();
            double currentMaxY = 0;
            int currentLineCount = 0;
            List<Line2D> columnParts = new LinkedList<>();
            allColumnParts.add(columnParts);
            for (int lineNumber = 0; lineNumber < gaps.size(); lineNumber++) {
                List<Rectangle2D> textBlocksInLine = gaps.get(lineNumber);
                if (anyBlockIntersectX(textBlocksInLine, x)) {
                    if (lineNumber == gaps.size() - 1) {
                        currentMaxY = mainBodyTextFrame.getMinY();
                    } else {
                        currentMaxY = gaps.get(lineNumber + 1).get(0).getMinY();
                    }
                    currentLineCount++;
                } else {
                    if (currentLineCount >= LINE_COUNT_THRESHOLD) {
                        columnParts.add(new Line2D.Double(x, currentMinY, x, currentMaxY));
                    }
                    currentMinY = gaps.get(lineNumber).get(0).getMaxY();
                    currentMaxY = currentMinY;
                    currentLineCount = 0;
                }

            }
            if (currentLineCount >= LINE_COUNT_THRESHOLD) {
                columnParts.add(new Line2D.Double(x, currentMinY, x, currentMaxY));
            }
        }
        return Stream.concat(Stream.of(mainBodyTextFrame),
                allColumnParts.stream()
                        .flatMap(columnParts -> columnParts.stream().map(line -> new Rectangle2D.Double(line.getX2(), line.getY2(), 1, Math.abs(line.getY2() - line.getY1()))))
                        .map(r -> (Rectangle2D) r)).toList();
    }


    private static boolean anyBlockIntersectX(List<Rectangle2D> textBlocksInLine, double x) {

        return textBlocksInLine.stream().anyMatch(gap -> gap.getMinX() < x && x < gap.getMaxX());
    }


    private double calculateGapLocation(int columnIndex, int numberOfColumns, double pageWidth) {

        return (pageWidth / numberOfColumns) * columnIndex;
    }

}
