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

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;

import com.knecon.fforesight.service.layoutparser.processor.docstrum.model.Character;
import com.knecon.fforesight.service.layoutparser.processor.docstrum.model.Line;
import com.knecon.fforesight.service.layoutparser.processor.docstrum.model.Zone;
import com.knecon.fforesight.service.layoutparser.processor.docstrum.service.LineBuilderService;
import com.knecon.fforesight.service.layoutparser.processor.docstrum.service.NearestNeighbourService;
import com.knecon.fforesight.service.layoutparser.processor.docstrum.service.ReadingOrderService;
import com.knecon.fforesight.service.layoutparser.processor.docstrum.service.SpacingService;
import com.knecon.fforesight.service.layoutparser.processor.docstrum.service.ZoneBuilderService;
import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings;
import com.knecon.fforesight.service.layoutparser.processor.model.text.RedTextPosition;
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextDirection;
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
import com.knecon.fforesight.service.layoutparser.processor.visualization.LayoutDebugLayer;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class DocstrumSegmentationService {

    public static final double SAME_DIRECTION_THRESHOLD = 0.9;
    private final NearestNeighbourService nearestNeighbourService;
    private final SpacingService spacingService;
    private final LineBuilderService lineBuilderService;
    private final ZoneBuilderService zoneBuilderService;
    private final ReadingOrderService readingOrderService;


    public List<Zone> segmentPage(List<Word> textPositions, boolean xyOrder, CleanRulings usedRulings, LayoutDebugLayer visualizations) {

        EnumMap<TextDirection, Integer> directionCounts = new EnumMap<>(TextDirection.class);

        List<Zone> newZones = computeZones(textPositions, usedRulings, visualizations, TextDirection.ZERO);
        directionCounts.put(TextDirection.ZERO, newZones.size());
        List<Zone> zones = new ArrayList<>(newZones);

        newZones = computeZones(textPositions, usedRulings, visualizations, TextDirection.QUARTER_CIRCLE);
        directionCounts.put(TextDirection.QUARTER_CIRCLE, newZones.size());
        zones.addAll(newZones);

        newZones = computeZones(textPositions, usedRulings, visualizations, TextDirection.HALF_CIRCLE);
        directionCounts.put(TextDirection.HALF_CIRCLE, newZones.size());
        zones.addAll(newZones);

        newZones = computeZones(textPositions, usedRulings, visualizations, TextDirection.THREE_QUARTER_CIRCLE);
        directionCounts.put(TextDirection.THREE_QUARTER_CIRCLE, newZones.size());
        zones.addAll(newZones);

        return readingOrderService.resolve(zones, xyOrder, mostSameDirection(directionCounts));
    }


    private boolean mostSameDirection(EnumMap<TextDirection, Integer> directionCounts) {

        int total = directionCounts.values()
                .stream()
                .mapToInt(i -> i).sum();

        if ((double) directionCounts.get(TextDirection.ZERO) / total > SAME_DIRECTION_THRESHOLD) {
            return true;
        } else if ((double) directionCounts.get(TextDirection.QUARTER_CIRCLE) / total > SAME_DIRECTION_THRESHOLD) {
            return true;
        } else if ((double) directionCounts.get(TextDirection.HALF_CIRCLE) / total > SAME_DIRECTION_THRESHOLD) {
            return true;
        } else if ((double) directionCounts.get(TextDirection.THREE_QUARTER_CIRCLE) / total > SAME_DIRECTION_THRESHOLD) {
            return true;
        }
        return false;
    }


    private List<Zone> computeZones(List<Word> textPositions, CleanRulings rulings, LayoutDebugLayer visualizations, TextDirection direction) {

        List<RedTextPosition> positions = textPositions.stream()
                .filter(t -> t.getDir() == direction)
                .map(Word::getTextPositions)
                .flatMap(List::stream)
                .toList();

        List<Character> characters = positions.stream()
                .map(Character::new)
                .collect(Collectors.toList());

        nearestNeighbourService.findNearestNeighbors(characters);

        double characterSpacing = spacingService.computeCharacterSpacing(characters);
        double lineSpacing = Math.min(spacingService.computeLineSpacing(characters), 20);

        List<Line> lines = lineBuilderService.buildLines(characters, characterSpacing, lineSpacing, rulings);
        return zoneBuilderService.buildZones(lines, characterSpacing, lineSpacing, rulings);
    }

}
