package com.knecon.fforesight.service.layoutparser.internal.api.data.redaction;

import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
@Schema(description = "Object containing information about the parsed tree structure of the SemanticNodes, such as Section, Paragraph, Headline etc inside of the document.")
public class DocumentStructure implements Serializable {

    @Schema(description = "The root EntryData represents the Document.")
    EntryData root;

    @Schema(description = "Object containing the extra field names, a table has in its properties field.")
    public static class TableProperties implements Serializable {

        public static final String NUMBER_OF_ROWS = "numberOfRows";
        public static final String NUMBER_OF_COLS = "numberOfCols";

    }

    @Schema(description = "Object containing the extra field names, an Image has in its properties field.")
    public static class ImageProperties implements Serializable {

        public static final String TRANSPARENT = "transparent";
        public static final String IMAGE_TYPE = "imageType";
        public static final String POSITION = "position";
        public static final String ID = "id";

    }

    @Schema(description = "Object containing the extra field names, a table cell has in its properties field.")
    public static class TableCellProperties implements Serializable {

        public static final String B_BOX = "bBox";
        public static final String ROW = "row";
        public static final String COL = "col";
        public static final String HEADER = "header";

    }

    @Schema(description = "Object containing the extra field names, a duplicate paragraph has in its properties field.")
    public static class DuplicateParagraphProperties implements Serializable {

        public static final String UNSORTED_TEXTBLOCK_ID = "utbid";

    }

    public static final String RECTANGLE_DELIMITER = ";";


    public static Rectangle2D parseRectangle2D(String bBox) {

        List<Float> floats = Arrays.stream(bBox.split(RECTANGLE_DELIMITER)).map(Float::parseFloat).toList();
        return new Rectangle2D.Float(floats.get(0), floats.get(1), floats.get(2), floats.get(3));
    }


    public EntryData get(List<Integer> tocId) {

        if (tocId.isEmpty()) {
            return root;
        }
        EntryData entry = root.children.get(tocId.get(0));
        for (int id : tocId.subList(1, tocId.size())) {
            entry = entry.children.get(id);
        }
        return entry;
    }


    public Stream<EntryData> streamAllEntries() {

        return Stream.concat(Stream.of(root), root.children.stream()).flatMap(DocumentStructure::flatten);
    }


    public String toString() {

        return String.join("\n", streamAllEntries().map(EntryData::toString).toList());
    }


    private static Stream<EntryData> flatten(EntryData entry) {

        return Stream.concat(Stream.of(entry), entry.children.stream().flatMap(DocumentStructure::flatten));
    }


    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @FieldDefaults(level = AccessLevel.PRIVATE)
    @Schema(description = "Object containing information of a SemanticNode and also structuring the layout with children.")
    public static class EntryData implements Serializable {

        @Schema(description = "Type of the semantic node.", allowableValues = {"DOCUMENT", "SECTION", "PARAGRAPH", "HEADLINE", "TABLE", "TABLE_CELL", "HEADER", "FOOTER", "IMAGE"})
        NodeType type;
        @Schema(description = "Specifies the position in the parsed tree structure.", example = "[1, 0, 2]")
        int[] treeId;
        @Schema(description = "Specifies the text block IDs associated with this semantic node. The value should be joined with the DocumentTextData/DocumentPositionData. Is empty, if no text block is directly associated with this semantic node. Only Paragraph, Headline, Header or Footer is directly associated with a text block.", example = "[1]")
        Long[] atomicBlockIds;
        @Schema(description = "Specifies the pages this semantic node appears on. The value should be joined with the PageData.", example = "[1, 2, 3]")
        Long[] pageNumbers;
        @Schema(description = "Some semantic nodes have additional information, this information is stored in this Map. The extra fields are specified by the Properties subclasses.", example = "For a Table: {\"numberOfRows\": 3, \"numberOfCols\": 4}")
        Map<String, String> properties;
        @Schema(description = "All child Entries of this Entry.", example = "[1, 2, 3]")
        List<EntryData> children;
        @Schema(description = "Describes the origin of the semantic node", example = "[ALGORITHM]")
        Set<LayoutEngine> engines;


        @Override
        public String toString() {

            StringBuilder sb = new StringBuilder();
            sb.append("[");
            for (int i : treeId) {
                sb.append(i);
                sb.append(",");
            }
            sb.delete(sb.length() - 1, sb.length());
            sb.append("]: ");

            sb.append(type);
            sb.append(" atbs = ");
            sb.append(atomicBlockIds.length);

            return sb.toString();
        }

    }

}
