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

import java.awt.geom.Rectangle2D;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
import com.knecon.fforesight.service.layoutparser.processor.visualization.LayoutparsingVisualizations;
import com.knecon.fforesight.service.layoutparser.processor.model.graph.DocumentTree;
import com.knecon.fforesight.service.layoutparser.processor.model.graph.entity.RedactionEntity;
import com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock.TextBlock;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class Document implements GenericSemanticNode {

    @Builder.Default
    Set<LayoutEngine> engines = new HashSet<>(Set.of(LayoutEngine.ALGORITHM));

    Set<Page> pages;
    DocumentTree documentTree;
    Integer numberOfPages;
    TextBlock textBlock;
    @Builder.Default
    Set<RedactionEntity> entities = new HashSet<>();

    LayoutparsingVisualizations visualizations;


    @Override
    public NodeType getType() {

        return NodeType.DOCUMENT;
    }


    public TextBlock getTextBlock() {

        if (textBlock == null) {
            textBlock = GenericSemanticNode.super.getTextBlock();
        }
        return textBlock;
    }


    public List<Section> getMainSections() {

        return streamChildrenOfType(NodeType.SECTION).map(node -> (Section) node)
                .collect(Collectors.toList());
    }


    public List<Header> getHeaders() {

        return streamChildrenOfType(NodeType.HEADER).map(node -> (Header) node)
                .collect(Collectors.toList());
    }


    public List<Footer> getFooters() {

        return streamChildrenOfType(NodeType.FOOTER).map(node -> (Footer) node)
                .collect(Collectors.toList());
    }


    public Stream<TextBlock> streamTerminalTextBlocksInOrder() {

        return streamAllNodes().filter(SemanticNode::isLeaf)
                .map(SemanticNode::getTextBlock);
    }


    @Override
    public List<Integer> getTreeId() {

        return Collections.emptyList();
    }


    @Override
    public void setTreeId(List<Integer> tocId) {

        throw new UnsupportedOperationException("Document is always the root of the TablePageBlock of Contents");
    }


    @Override
    public Headline getHeadline() {

        return streamAllSubNodesOfType(NodeType.HEADLINE).map(node -> (Headline) node)
                .findFirst()
                .orElse(Headline.builder().build());
    }


    private Stream<SemanticNode> streamAllNodes() {

        return documentTree.allEntriesInOrder()
                .map(DocumentTree.Entry::getNode);
    }


    public Stream<Image> streamAllImages() {

        return streamAllSubNodesOfType(NodeType.IMAGE).map(node -> (Image) node);
    }


    public Map<NodeType, Long> buildSemanticNodeCounts() {

        return streamAllSubNodes().collect(Collectors.groupingBy(SemanticNode::getType, Collectors.counting()));
    }


    @Override
    public String toString() {

        return NodeType.DOCUMENT + ": " + this.getTextBlock().buildSummary();
    }


    @Override
    public Map<Page, Rectangle2D> getBBox() {

        Map<Page, Rectangle2D> bBox = new HashMap<>();
        for (Page page : pages) {
            bBox.put(page, new Rectangle2D.Double(0, 0, page.getWidth(), page.getHeight()));
        }
        return bBox;
    }

}
