package com.knecon.fforesight.service.layoutparser.processor.services.mapper;

import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.LinkedList;
import java.util.Map;
import java.util.Optional;

import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Document;
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Headline;
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Page;
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Section;
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.SemanticNode;
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.SuperSection;
import com.knecon.fforesight.service.layoutparser.processor.utils.CoordinateTransforms;
import com.knecon.fforesight.service.layoutparser.processor.utils.PageInformation;
import com.knecon.fforesight.service.viewerdoc.model.Outline;

import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;

@UtilityClass
public class OutlineMapper {

    public Outline createOutline(Document document) {

        Outline outline = new Outline();
        addChildren(document, null, outline);
        return outline;
    }


    public void addChildren(SemanticNode parentNode, Outline.Entry parentEntry, Outline outline) {

        parentNode.streamChildren()
                .filter(child -> child instanceof Section || child instanceof SuperSection)
                .forEach(child -> {
                    Optional<Headline> headline = findHeadline(child);
                    if (headline.isPresent()) {
                        Outline.Entry entry = buildEntry(child.getHeadline());
                        if (parentEntry != null) {
                            parentEntry.children().add(entry);
                        } else {
                            outline.getEntries().add(entry);
                        }
                        addChildren(child, entry, outline);
                    } else {
                        addChildren(child, parentEntry, outline);
                    }
                });
    }


    private static Optional<Headline> findHeadline(SemanticNode child) {

        return child.streamChildren()
                .filter(node -> node instanceof Headline)
                .map(node -> (Headline) node)
                .findFirst();
    }


    @SneakyThrows
    private Outline.Entry buildEntry(Headline headline) {

        Map<Page, Rectangle2D> bbox = headline.getBBox();
        Rectangle2D r = bbox.get(headline.getFirstPage());
        Point2D.Double position = new Point2D.Double(r.getMinX(), r.getMaxY());
        PageInformation pageInformation = PageInformation.fromPage(headline.getFirstPage());

        AffineTransform pdfToPage = CoordinateTransforms.calculateInitialUserSpaceCoordsToPageCoords(pageInformation);
        pdfToPage.transform(position, position);

        AffineTransform mirror = new AffineTransform(1, 0, 0, -1, 0, pageInformation.heightRot());
        mirror.transform(position, position);

        AffineTransform.getTranslateInstance(0, 5).transform(position, position);

        Outline.JumpAction action = new Outline.JumpAction(headline.getFirstPage().getNumber(), position);
        return new Outline.Entry(headline.getTextBlock().getSearchText(), action, new LinkedList<>());
    }

}
