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

import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
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.FloatFrequencyCounter;
import com.knecon.fforesight.service.layoutparser.processor.model.PageBlockType;
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<TextPositionSequence> sequences = new ArrayList<>();

    private String mostPopularWordFont;

    private String mostPopularWordStyle;

    private float mostPopularWordFontSize;

    private float mostPopularWordHeight;

    private float mostPopularWordSpaceWidth;

    private float highestFontSize;

    private PageBlockType classification;

    private boolean toDuplicate;


    public TextPageBlock(List<TextPositionSequence> sequences) {

        this.sequences = sequences;
        calculateFrequencyCounters();
        calculateBBox();
    }


    @JsonIgnore
    public TextDirection getDir() {

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


    @JsonIgnore
    public float getPageHeight() {

        return sequences.get(0).getPageHeight();
    }


    @JsonIgnore
    public float getPageWidth() {

        return sequences.get(0).getPageWidth();
    }


    private void calculateBBox() {

        if (sequences == null) {
            this.bBox = new Rectangle2D.Double();
            this.bBoxInitialUserSpace = new Rectangle2D.Double();
            return;
        }
        setToBBoxOfComponents(sequences);
    }


    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<TextPositionSequence> sequences = textBlocksToMerge.stream()
                .map(TextPageBlock::getSequences)
                .flatMap(java.util.Collection::stream)
                .toList();
        sequences = new ArrayList<>(sequences);

        return new TextPageBlock(sequences);
    }


    private void calculateFrequencyCounters() {

        FloatFrequencyCounter lineHeightFrequencyCounter = new FloatFrequencyCounter();
        FloatFrequencyCounter fontSizeFrequencyCounter = new FloatFrequencyCounter();
        FloatFrequencyCounter spaceFrequencyCounter = new FloatFrequencyCounter();
        StringFrequencyCounter fontFrequencyCounter = new StringFrequencyCounter();
        StringFrequencyCounter styleFrequencyCounter = new StringFrequencyCounter();

        for (TextPositionSequence wordBlock : sequences) {

            lineHeightFrequencyCounter.add(wordBlock.getTextHeight());
            fontSizeFrequencyCounter.add(wordBlock.getFontSize());
            spaceFrequencyCounter.add(wordBlock.getSpaceWidth());
            fontFrequencyCounter.add(wordBlock.getFont());
            styleFrequencyCounter.add(wordBlock.getFontStyle());

        }

        setMostPopularWordFont(fontFrequencyCounter.getMostPopular());
        setMostPopularWordStyle(styleFrequencyCounter.getMostPopular());
        setMostPopularWordFontSize(fontSizeFrequencyCounter.getMostPopular());
        setMostPopularWordHeight(lineHeightFrequencyCounter.getMostPopular());
        setMostPopularWordSpaceWidth(spaceFrequencyCounter.getMostPopular());
        setHighestFontSize(fontSizeFrequencyCounter.getHighest());
    }


    public TextPageBlock union(TextPositionSequence r) {

        TextPageBlock union = this.copy();
        union.getSequences().add(r);
        calculateFrequencyCounters();
        calculateBBox();
        return union;
    }


    public TextPageBlock union(TextPageBlock r) {

        TextPageBlock union = this.copy();
        union.getSequences().addAll(r.getSequences());
        calculateFrequencyCounters();
        calculateBBox();
        return union;
    }


    public void add(TextPageBlock r) {

        sequences.addAll(r.getSequences());
        calculateFrequencyCounters();
        calculateBBox();
    }


    public void add(TextPositionSequence r) {

        sequences.add(r);
        calculateFrequencyCounters();
        calculateBBox();
    }


    public TextPageBlock copy() {

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


    @Override
    public String toString() {

        StringBuilder builder = new StringBuilder();

        for (int i = 0; i < sequences.size(); i++) {
            String sequenceAsString = sequences.get(i).toString();
            // Fix for missing Whitespace. This is recognized in getSequences method. See PDFTextStripper Line 1730.
            if (i != 0 && sequences.get(i - 1).charAt(sequences.get(i - 1).length() - 1) != ' ' && sequenceAsString.charAt(0) != ' ') {
                builder.append(' ');
            }
            builder.append(sequenceAsString);
        }

        return builder.toString();

    }


    @Override
    @JsonIgnore
    public String getText() {

        StringBuilder sb = new StringBuilder();

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

        return TextNormalizationUtilities.removeHyphenLineBreaks(sb.toString());
    }


    public int getNumberOfLines() {

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


    @Override
    public boolean isEmpty() {

        return sequences.isEmpty();
    }

}
