package com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock;

import static java.lang.String.format;

import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import com.knecon.fforesight.service.layoutparser.processor.model.graph.Boundary;
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Page;

public interface TextBlock extends CharSequence {

    String getSearchText();


    List<AtomicTextBlock> getAtomicTextBlocks();


    List<Boundary> getBoldTextBoundaries();


    List<Boundary> getItalicTextBoundaries();


    String getOrientation();


    int getTextDirection();


    Boundary getBoundary();


    int getNextLinebreak(int fromIndex);


    int getPreviousLinebreak(int fromIndex);


    List<Integer> getLineBreaks();


    Rectangle2D getPosition(int stringIdx);


    List<Rectangle2D> getPositions(Boundary stringBoundary);


    Map<Page, List<Rectangle2D>> getPositionsPerPage(Boundary stringBoundary);


    int numberOfLines();


    default int indexOf(String searchTerm) {

        return indexOf(searchTerm, getBoundary().start());
    }


    default Set<Page> getPages() {

        return getAtomicTextBlocks().stream().map(AtomicTextBlock::getPage).collect(Collectors.toUnmodifiableSet());
    }


    default Set<Page> getPages(Boundary boundary) {

        return getAtomicTextBlocks().stream()
                .filter(atomicTextBlock -> atomicTextBlock.getBoundary().intersects(boundary))
                .map(AtomicTextBlock::getPage)
                .collect(Collectors.toUnmodifiableSet());
    }


    default int indexOf(String searchTerm, int startOffset) {

        int start = getSearchText().indexOf(searchTerm, startOffset - getBoundary().start());
        if (start == -1) {
            return -1;
        }
        return start + getBoundary().start();
    }


    default CharSequence getFirstLine() {

        return subSequence(getBoundary().start(), getNextLinebreak(getBoundary().start()));
    }


    default boolean containsBoundary(Boundary boundary) {

        if (boundary.end() < boundary.start()) {
            throw new IllegalArgumentException(format("Invalid %s, StartIndex must be smaller than EndIndex", boundary));
        }
        return getBoundary().contains(boundary);
    }


    default boolean containsIndex(int stringIndex) {

        return getBoundary().contains(stringIndex);
    }


    default CharSequence subSequence(Boundary boundary) {

        return subSequence(boundary.start(), boundary.end());
    }


    default String buildSummary() {

        String[] words = getSearchText().split(" ");
        int bound = Math.min(words.length, 4);
        List<String> list = new ArrayList<>(Arrays.asList(words).subList(0, bound));

        return String.join(" ", list);
    }


    @Override
    default CharSequence subSequence(int start, int end) {

        return getSearchText().substring(start - getBoundary().start(), end - getBoundary().start());
    }


    @Override
    default int length() {

        return getBoundary().length();
    }


    @Override
    default char charAt(int index) {

        return getSearchText().charAt(index - getBoundary().start());
    }

}
