package com.knecon.fforesight.service.layoutparser.processor.visualization;

import java.awt.Color;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDMarkedContent;

import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.Rectangle;
import com.knecon.fforesight.service.layoutparser.processor.docstrum.model.BoundingBox;
import com.knecon.fforesight.service.layoutparser.processor.docstrum.model.Line;
import com.knecon.fforesight.service.layoutparser.processor.docstrum.model.Zone;
import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings;
import com.knecon.fforesight.service.layoutparser.processor.model.table.Ruling;
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageBlock;
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
import com.knecon.fforesight.service.layoutparser.processor.utils.MarkedContentUtils;
import com.knecon.fforesight.service.layoutparser.processor.utils.RectangleTransformations;
import com.knecon.fforesight.service.viewerdoc.layers.LayoutDebugLayerConfig;
import com.knecon.fforesight.service.viewerdoc.model.ColoredLine;
import com.knecon.fforesight.service.viewerdoc.model.ColoredRectangle;
import com.knecon.fforesight.service.viewerdoc.model.PlacedText;
import com.knecon.fforesight.service.viewerdoc.model.VisualizationsOnPage;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.FieldDefaults;

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class LayoutDebugLayer extends LayoutDebugLayerConfig {

    boolean active;


    public void addTextVisualizations(List<TextPositionSequence> textPositionSequences, int pageNumber) {

        if (!active) {
            return;
        }
        VisualizationsOnPage visualizationsOnPage = getOrCreateVisualizationsOnPage(pageNumber, this.words);
        visualizationsOnPage.getColoredRectangles()
                .addAll(textPositionSequences.stream()
                                .map(BoundingBox::getBBoxInitialUserSpace)
                                .map(rect -> new ColoredRectangle(rect, WORDS_COLOR, 1))
                                .toList());
    }


    public void addCleanRulingVisualization(CleanRulings cleanRulings, int pageNumber) {

        if (!active) {
            return;
        }
        VisualizationsOnPage visualizationsOnPage = getOrCreateVisualizationsOnPage(pageNumber, this.clean_rulings);
        visualizationsOnPage.getColoredLines()
                .addAll(cleanRulings.buildAll()
                                .stream()
                                .map(ruling -> new ColoredLine(ruling, decideOnRulingColor(ruling), 0.5f))
                                .toList());
    }


    public void addRulingVisualization(List<Ruling> rulings, int pageNumber) {

        if (!active) {
            return;
        }
        VisualizationsOnPage visualizationsOnPage = getOrCreateVisualizationsOnPage(pageNumber, this.rulings);
        visualizationsOnPage.getColoredLines()
                .addAll(rulings.stream()
                                .map(ruling -> new ColoredLine(ruling, decideOnRulingColor(ruling), 0.5f))
                                .toList());
    }


    private Color decideOnRulingColor(Ruling ruling) {

        return switch (ruling.getClassification()) {
            case TABLE_LINE -> TABLE_RULINGS_COLOR;
            case HEADER_SEPARATOR -> HEADER_RULING_COLOR;
            case FOOTER_SEPARATOR -> FOOTER_RULING_COLOR;
            case UNDERLINE -> UNDERLINE_RULING_COLOR;
            case STRIKETROUGH -> STRIKETROUGH_RULING_COLOR;
            default -> RULINGS_COLOR;
        };
    }


    public void addCellVisualizations(List<? extends BoundingBox> cells, int pageNumber) {

        if (!active) {
            return;
        }
        VisualizationsOnPage visualizationsOnPage = getOrCreateVisualizationsOnPage(pageNumber, this.cells);
        visualizationsOnPage.getColoredRectangles()
                .addAll(cells.stream()
                                .map(cell -> new ColoredRectangle(cell.getBBoxInitialUserSpace(), CELLS_COLOR, 1))
                                .toList());
    }


    public void addZoneVisualizations(List<Zone> zones, int page) {

        if (!active) {
            return;
        }

        VisualizationsOnPage visualizationsOnPage = getOrCreateVisualizationsOnPage(page, this.zones);
        visualizationsOnPage.getColoredRectangles()
                .addAll(zones.stream()
                                .map(BoundingBox::getBBoxInitialUserSpace)
                                .map(zone -> new ColoredRectangle(zone, ZONES_COLOR, 1))
                                .toList());

    }


    public void addLineVisualizationsFromZones(List<Zone> zones, int page) {

        addLineVisualizations(zones.stream()
                                      .map(Zone::getLines)
                                      .flatMap(Collection::stream)
                                      .toList(), page);

    }


    public void addLineVisualizations(List<Line> lines, int page) {

        if (!active) {
            return;
        }
        VisualizationsOnPage visualizationsOnPage = getOrCreateVisualizationsOnPage(page, this.lines);
        visualizationsOnPage.getColoredRectangles()
                .addAll(lines.stream()
                                .map(BoundingBox::getBBoxInitialUserSpace)
                                .map(line -> new ColoredRectangle(line, LINES_COLOR, 0.5f))
                                .toList());
    }


    public void addTextBlockVisualizations(List<TextPageBlock> textPageBlocks, int page) {

        if (!active) {
            return;
        }
        VisualizationsOnPage visualizationsOnPage = getOrCreateVisualizationsOnPage(page, zones);
        visualizationsOnPage.getColoredRectangles()
                .addAll(textPageBlocks.stream()
                                .map(rect -> new ColoredRectangle(rect.getBBoxInitialUserSpace(), ZONES_COLOR, 1))
                                .toList());
    }


    public void addMainBodyVisualization(Rectangle rectangle, int pageNumber) {

        if (!active) {
            return;
        }
        VisualizationsOnPage visualizationsOnPage = getOrCreateVisualizationsOnPage(pageNumber, mainBody);
        visualizationsOnPage.getColoredRectangles()
                .add(new ColoredRectangle(new Rectangle2D.Double(rectangle.getTopLeft().getX(), rectangle.getTopLeft().getY(), rectangle.getWidth(), rectangle.getHeight()),
                                          MAIN_BODY_COLOR,
                                          1));
    }


    public void addMarkedContentVisualizations(List<PDMarkedContent> markedContents, int pageNumber) {

        if (!active) {
            return;
        }

        VisualizationsOnPage visualizationsOnPage = getOrCreateVisualizationsOnPage(pageNumber, markedContent);

        List<MarkedContentUtils.MarkedContentPosition> markedContentBBoxMapBySubType = MarkedContentUtils.getMarkedContentPositions(markedContents);
        AtomicInteger count = new AtomicInteger();
        markedContentBBoxMapBySubType.forEach(markedContentPosition -> {
                                                  var bbox = markedContentPosition.textPositions()
                                                          .stream()
                                                          .collect(RectangleTransformations.collectBBox());
                                                  String type = markedContentPosition.formattedType() + " " + count.getAndIncrement();

                                                  float translationAmount = ((FONT.getStringWidth(type) / 100) + 6);
                                                  // Pushes the string to the left of the box: calculate string width, divide by font units (1000), multiply with font size (10), add small offset (6).

                                                  visualizationsOnPage.getPlacedTexts()
                                                          .add(PlacedText.textFacingUp(type, new Point2D.Double(bbox.getX() - translationAmount, bbox.getY() + bbox.getHeight()), 10, Color.BLACK, FONT));

                                                  visualizationsOnPage.getColoredRectangles().add(new ColoredRectangle(bbox, MARKED_CONTENT_COLOR, 1));
                                              }

        );

    }


    public void addCharactersWithNeighbours(List<Zone> zones, int page) {

        if (!active) {
            return;
        }

        VisualizationsOnPage characterVisualizations = getOrCreateVisualizationsOnPage(page, characters);
        VisualizationsOnPage neighbourVisualizations = getOrCreateVisualizationsOnPage(page, neighbours);

        AtomicInteger index = new AtomicInteger(0);
        zones.forEach(zone -> zone.getLines()
                .stream()
                .map(Line::getCharacters)
                .flatMap(Collection::stream)
                .forEach(character -> {
                    Color color = ROTATING_CHARACTER_COLOR.get(index.getAndIncrement() % ROTATING_CHARACTER_COLOR.size());
                    Rectangle2D charBBox = character.getTextPosition().getBBoxInitialUserSpace();
                    characterVisualizations.getColoredRectangles().add(new ColoredRectangle(charBBox, color, 1));
                    character.getNeighbors()
                            .forEach(neighbor -> {
                                Rectangle2D neighborBBox = neighbor.getCharacter().getTextPosition().getBBoxInitialUserSpace();
                                Line2D line = new Line2D.Double(new Point2D.Double(charBBox.getCenterX(), charBBox.getCenterY()),
                                                                new Point2D.Double(neighborBBox.getCenterX(), neighborBBox.getCenterY()));
                                neighbourVisualizations.getColoredLines().add(new ColoredLine(line, color, 1));
                            });
                }));

    }

}
