package com.knecon.fforesight.service.layoutparser.processor.model.text;

import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.knecon.fforesight.service.layoutparser.processor.model.AbstractPageBlock;
import com.knecon.fforesight.service.layoutparser.processor.model.PageBlockType;
import com.knecon.fforesight.service.layoutparser.processor.utils.RectangleTransformations;
import com.knecon.fforesight.service.layoutparser.processor.utils.TextNormalizationUtilities;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@Builder
@NoArgsConstructor
public class TextPageBlock extends AbstractPageBlock {

    @Builder.Default
    private List<Word> words = new ArrayList<>();
    @Builder.Default
    private FrequencyCounters frequencyCounters = new FrequencyCounters();

    private Rectangle2D bBoxDirAdj;

    private boolean underlined;

    private PageBlockType classification;

    private boolean toDuplicate;

    private String text;
    private boolean changed;


    public TextPageBlock(List<Word> words) {

        this.words = new ArrayList<>(words);
        this.frequencyCounters = new FrequencyCounters();

        if (!words.isEmpty()) {
            addToFrequencyCounters(words);
        }
        calculateBBox();
    }


    public List<Word> getWords() {

        return Collections.unmodifiableList(words);
    }


    public TextDirection getDir() {

        return words.get(0).getDir();
    }


    private void calculateBBox() {

        if (words == null) {
            this.bBox = new Rectangle2D.Double();
            this.bBoxPdf = new Rectangle2D.Double();
            this.bBoxDirAdj = new Rectangle2D.Double();
            return;
        }
        this.bBoxDirAdj = words.stream()
                .map(Word::getBBoxDirAdj)
                .collect(RectangleTransformations.collectBBox());

        setToBBoxOfComponents(words);
    }


    public void recalculateBBox() {

        calculateBBox();
    }


    public static TextPageBlock merge(List<TextPageBlock> textBlocksToMerge) {

        if (textBlocksToMerge.isEmpty()) {
            throw new IllegalArgumentException("Need to provide at least one TextPageBlock.");
        }
        if (textBlocksToMerge.stream()
                    .map(AbstractPageBlock::getPage)
                    .distinct()
                    .count() != 1) {
            throw new IllegalArgumentException("Cannot merge textBlocks on different pages.");
        }

        List<Word> sequences = textBlocksToMerge.stream()
                .map(TextPageBlock::getWords)
                .flatMap(java.util.Collection::stream)
                .toList();
        sequences = new ArrayList<>(sequences);

        return new TextPageBlock(sequences);
    }


    private void addToFrequencyCounters(List<Word> sequences) {

        for (Word wordBlock : sequences) {

            frequencyCounters.getLineHeightFrequencyCounter().add(wordBlock.getTextHeight());
            frequencyCounters.getFontSizeFrequencyCounter().add(wordBlock.getFontSize());
            frequencyCounters.getSpaceFrequencyCounter().add(wordBlock.getSpaceWidth());
            frequencyCounters.getFontFrequencyCounter().add(wordBlock.getFont());
            frequencyCounters.getStyleFrequencyCounter().add(wordBlock.getFontStyle());
        }

        setUnderlined(this.words.stream()
                              .allMatch(Word::isUnderline));
    }


    public TextPageBlock union(Word r) {

        TextPageBlock union = this.copy();
        union.add(r);
        addToFrequencyCounters(List.of(r));
        calculateBBox();
        return union;
    }


    public TextPageBlock union(TextPageBlock r) {

        TextPageBlock union = this.copy();
        union.addAll(r.getWords());
        addToFrequencyCounters(r.getWords());
        calculateBBox();
        return union;
    }


    public void add(TextPageBlock textPageBlock) {

        changed = true;
        words.addAll(textPageBlock.getWords());
        addToFrequencyCounters(textPageBlock.getWords());
        calculateBBox();
    }


    public void add(Word word) {

        changed = true;
        words.add(word);
        addToFrequencyCounters(List.of(word));
        calculateBBox();
    }


    public void addAll(List<Word> words) {

        changed = true;
        this.words.addAll(words);
        addToFrequencyCounters(words);
        calculateBBox();
    }


    public TextPageBlock copy() {

        return new TextPageBlock(new ArrayList<>(words));
    }


    @Override
    public String toString() {

        return getText();
    }


    @Override
    @JsonIgnore
    public String getText() {

        if (text == null || changed) {

            StringBuilder sb = new StringBuilder();

            Word previous = null;
            for (Word word : words) {
                if (previous != null) {
                    if (Math.abs(previous.getMaxYDirAdj() - word.getMaxYDirAdj()) > word.getTextHeight()) {
                        sb.append('\n');
                    } else {
                        sb.append(' ');
                    }
                }
                sb.append(word.toString());
                previous = word;
            }

            text = TextNormalizationUtilities.removeHyphenLinebreaks(sb.toString());
            changed = false;
        }

        return text;
    }


    public int getNumberOfLines() {

        int numberOfLines = 1;
        Word previous = null;
        for (Word word : words) {
            if (previous != null) {
                if (word.getMaxYDirAdj() - previous.getMaxYDirAdj() > word.getTextHeight()) {
                    numberOfLines++;
                }
            }
            previous = word;
        }
        return numberOfLines;
    }


    public String getMostPopularWordFont() {

        return frequencyCounters.getFontFrequencyCounter().getMostPopular();
    }


    public String getMostPopularWordStyle() {

        return frequencyCounters.getStyleFrequencyCounter().getMostPopular();
    }


    public double getMostPopularWordFontSize() {

        return frequencyCounters.getFontSizeFrequencyCounter().getMostPopular();
    }


    public double getMostPopularWordHeight() {

        return frequencyCounters.getLineHeightFrequencyCounter().getMostPopular();
    }


    public double getMostPopularWordSpaceWidth() {

        return frequencyCounters.getSpaceFrequencyCounter().getMostPopular();
    }


    public double getHighestFontSize() {

        Double highest = frequencyCounters.getFontSizeFrequencyCounter().getHighest();
        return highest == null ? 0 : highest;
    }


    @Override
    public boolean isEmpty() {

        return words.isEmpty();
    }

}
