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

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

import com.knecon.fforesight.service.layoutparser.processor.model.text.TextDirection;
import com.knecon.fforesight.service.layoutparser.processor.utils.RectangleTransformations;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

@Getter
@Setter
@SuperBuilder
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
public abstract class TextBoundingBox extends BoundingBox {

    protected Rectangle2D bBoxDirAdj;

    protected TextDirection dir;


    @Override
    public void setToBBoxOfComponents(List<? extends BoundingBox> components) {

        super.setToBBoxOfComponents(components);
        this.bBoxDirAdj = components.stream()
                .filter(c -> c instanceof TextBoundingBox)
                .map(c -> (TextBoundingBox) c)
                .map(TextBoundingBox::getBBoxDirAdj)
                .collect(RectangleTransformations.collectBBox());

        Set<TextDirection> textDirections = components.stream()
                .filter(c -> c instanceof TextBoundingBox)
                .map(c -> (TextBoundingBox) c)
                .map(TextBoundingBox::getDir)
                .collect(Collectors.toSet());

        if (textDirections.isEmpty()) {
            dir = TextDirection.ZERO;
        } else if (textDirections.size() > 1) {
            throw new IllegalArgumentException("More than one text direction found");
        } else {
            dir = textDirections.iterator().next();
        }
    }


    public double getXDirAdj() {

        return this.bBoxDirAdj.getX();
    }


    public double getYDirAdj() {

        return this.bBoxDirAdj.getY();
    }


    public double getWidthDirAdj() {

        return this.bBoxDirAdj.getWidth();
    }


    public double getHeightDirAdj() {

        return this.bBoxDirAdj.getHeight();
    }


    public double getMaxXDirAdj() {

        return this.bBoxDirAdj.getMaxX();
    }


    public double getMaxYDirAdj() {

        return this.bBoxDirAdj.getMaxY();
    }


    public double getCenterYDirAdj() {

        return this.bBoxDirAdj.getCenterY();
    }


    public double getCenterXDirAdj() {

        return this.bBoxDirAdj.getCenterX();
    }


    public double horizontalDistanceDirAdj(TextBoundingBox other) {

        double rect1Right = getMaxXDirAdj();
        double rect1Left = getXDirAdj();
        double rect2Right = other.getMaxXDirAdj();
        double rect2Left = other.getXDirAdj();

        if (rect1Left > rect2Right || rect2Left > rect1Right) {
            return Math.max(rect2Left - rect1Right, rect1Left - rect2Right);
        } else {
            return 0;
        }
    }


    public double verticalDistanceDirAdj(TextBoundingBox other) {

        double rect1Top = getMaxYDirAdj();
        double rect1Bottom = getYDirAdj();
        double rect2Top = other.getMaxYDirAdj();
        double rect2Bottom = other.getYDirAdj();

        if (rect1Bottom > rect2Top || rect2Bottom > rect1Top) {
            return Math.max(rect2Bottom - rect1Top, rect1Bottom - rect2Top);
        } else {
            return 0;
        }
    }


    public boolean intersectsDirAdj(TextBoundingBox other) {

        return this.intersectsXDirAdj(other) && this.intersectsYDirAdj(other);
    }


    public boolean intersectsDirAdj(TextBoundingBox other, float yThreshold, float xThreshold) {

        return this.intersectsXDirAdj(other, xThreshold) && this.intersectsYDirAdj(other, yThreshold);
    }


    public boolean intersectsXDirAdj(TextBoundingBox other, float threshold) {

        return this.getXDirAdj() - threshold <= other.getMaxXDirAdj() && this.getMaxXDirAdj() + threshold >= other.getXDirAdj();
    }


    public boolean intersectsXDirAdj(TextBoundingBox other) {

        return this.getXDirAdj() <= other.getMaxXDirAdj() && this.getMaxXDirAdj() >= other.getXDirAdj();
    }


    public boolean intersectsYDirAdj(TextBoundingBox other) {

        return this.getYDirAdj() <= other.getMaxYDirAdj() && this.getMaxYDirAdj() >= other.getYDirAdj();
    }


    public boolean intersectsYDirAdj(TextBoundingBox other, float threshold) {

        return this.getYDirAdj() - threshold <= other.getMaxYDirAdj() && this.getMaxYDirAdj() + threshold >= other.getYDirAdj();
    }


    public boolean isAboveDirAdj(TextBoundingBox other) {

        return other.isBelow(this);
    }


    public boolean isBelowDirAdj(TextBoundingBox other) {

        return this.intersectsXDirAdj(other) && this.getYDirAdj() >= other.getMaxYDirAdj();
    }

}
