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

import static com.knecon.fforesight.service.layoutparser.processor.model.text.RedTextPosition.HEIGHT_PADDING;

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

import org.apache.pdfbox.text.TextPosition;

import com.knecon.fforesight.service.layoutparser.processor.docstrum.model.TextBoundingBox;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true) // needs the bbox to be unique
public class TextPositionSequence extends TextBoundingBox implements CharSequence {

    public static final String STANDARD = "standard";
    public static final String BOLD_ITALIC = "bold, italic";
    public static final String BOLD = "bold";
    public static final String ITALIC = "italic";

    @EqualsAndHashCode.Include
    private int page;

    @EqualsAndHashCode.Include
    @Builder.Default
    private List<RedTextPosition> textPositions = new ArrayList<>();

    private boolean isParagraphStart;
    private boolean strikethrough;
    private boolean underline;


    public TextPositionSequence(List<TextPosition> textPositions, int pageNumber, boolean isParagraphStart) {

        this.textPositions = textPositions.stream()
                .map(RedTextPosition::fromTextPosition)
                .collect(Collectors.toList());
        this.page = pageNumber;
        this.isParagraphStart = isParagraphStart;
        calculateBBox();
    }


    private void calculateBBox() {

        setToBBoxOfComponents(getTextPositions());
    }


    public TextPositionSequence(List<RedTextPosition> textPositions, int page) {

        this.textPositions = textPositions;
        this.page = page;
        calculateBBox();
    }


    @Override
    public int length() {

        return textPositions.size();
    }


    @Override
    public char charAt(int index) {

        RedTextPosition textPosition = textPositionAt(index);
        String text = textPosition.getUnicode();
        return text.charAt(0);
    }


    public char charAt(int index, boolean caseInSensitive) {

        RedTextPosition textPosition = textPositionAt(index);
        String text = textPosition.getUnicode();
        return caseInSensitive ? text.toLowerCase(Locale.ROOT).charAt(0) : text.charAt(0);
    }


    @Override
    public TextPositionSequence subSequence(int start, int end) {

        var textPositionSequence = new TextPositionSequence();
        textPositionSequence.textPositions = textPositions.subList(start, end);
        textPositionSequence.page = page;
        textPositionSequence.dir = dir;
        textPositionSequence.setToBBoxOfComponents(getTextPositions());
        return textPositionSequence;
    }


    @Override
    public String toString() {

        StringBuilder builder = new StringBuilder(length());
        for (int i = 0; i < length(); i++) {
            builder.append(charAt(i));
        }
        return builder.toString();
    }


    public RedTextPosition textPositionAt(int index) {

        return textPositions.get(index);
    }


    public void add(TextPositionSequence textPositionSequence, RedTextPosition textPosition) {

        this.textPositions.add(textPosition);
        this.page = textPositionSequence.getPage();
        calculateBBox();
    }


    public void add(TextPosition textPosition) {

        this.textPositions.add(RedTextPosition.fromTextPosition(textPosition));
        calculateBBox();
    }

    public double getTextHeightNoPadding() {

        return textPositions.get(0).getHeightDirAdj();
    }


    public double getTextHeight() {

        return textPositions.get(0).getHeightDirAdj() + HEIGHT_PADDING;
    }


    public String getFont() {

        if (textPositions.get(0).getFontName() == null) {
            return "none";
        }
        return textPositions.get(0).getFontName().toLowerCase(Locale.ROOT).replaceAll(",bold", "").replaceAll(",italic", "");
    }


    public String getFontStyle() {

        if (textPositions.get(0).getFontName() == null) {
            return STANDARD;
        }
        String lowercaseFontName = textPositions.get(0).getFontName().toLowerCase(Locale.ROOT);

        if (lowercaseFontName.contains(BOLD) && lowercaseFontName.contains(ITALIC)) {
            return BOLD_ITALIC;
        } else if (lowercaseFontName.contains(BOLD)) {
            return BOLD;
        } else if (lowercaseFontName.contains(ITALIC)) {
            return ITALIC;
        } else {
            return STANDARD;
        }
    }


    public float getFontSize() {

        return textPositions.get(0).getFontSizeInPt();
    }


    public float getSpaceWidth() {

        return textPositions.get(0).getWidthOfSpace();
    }

}

