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

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;

import org.springframework.lang.NonNull;

import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageBlock;

import lombok.Data;
import lombok.RequiredArgsConstructor;

@Data
@RequiredArgsConstructor
public class SectionTree implements Iterable<SectionTreeEntry> {

    private List<SectionTreeEntry> mainSections = new ArrayList<>();


    public SectionTree(List<SectionTreeEntry> mainSections) {

        this.mainSections = mainSections;
    }


    public List<TextPageBlock> getAllTextPageBlocks() {

        List<TextPageBlock> allTextPageBlocks = new ArrayList<>();
        for (SectionTreeEntry item : mainSections) {
            collectTextPageBlocks(item, allTextPageBlocks);
        }
        return allTextPageBlocks;
    }


    private void collectTextPageBlocks(SectionTreeEntry item, List<TextPageBlock> textPageBlocks) {

        textPageBlocks.add(item.getHeadline());
        for (SectionTreeEntry child : item.getChildren()) {
            collectTextPageBlocks(child, textPageBlocks);
        }
    }


    public List<SectionTreeEntry> getAllTableOfContentItems() {

        List<SectionTreeEntry> allItems = new ArrayList<>();
        for (SectionTreeEntry item : mainSections) {
            collectTableOfContentItems(item, allItems);
        }
        return allItems;
    }


    private void collectTableOfContentItems(SectionTreeEntry item, List<SectionTreeEntry> allItems) {

        allItems.add(item);
        for (SectionTreeEntry child : item.getChildren()) {
            collectTableOfContentItems(child, allItems);
        }
    }


    private boolean containsBlock(TextPageBlock block) {

        for (SectionTreeEntry existingItem : this.getMainSections()) {
            if (existingItem.getHeadline().equals(block) || existingItem.contains(block)) {
                return true;
            }
        }
        return false;
    }


    private boolean containsItem(SectionTreeEntry tocItem) {

        for (SectionTreeEntry existingItem : this.getMainSections()) {
            if (existingItem.equals(tocItem) || existingItem.contains(tocItem)) {
                return true;
            }
        }
        return false;
    }


    @Override
    public @NonNull Iterator<SectionTreeEntry> iterator() {

        return new SectionTreeEntryIterator(mainSections);
    }


    private static class SectionTreeEntryIterator implements Iterator<SectionTreeEntry> {

        private final Stack<Iterator<SectionTreeEntry>> stack = new Stack<>();


        SectionTreeEntryIterator(List<SectionTreeEntry> mainSections) {

            stack.push(mainSections.iterator());
        }


        @Override
        public boolean hasNext() {

            ensureStackTopIsCurrent();
            return !stack.isEmpty() && stack.peek().hasNext();
        }


        @Override
        public SectionTreeEntry next() {

            ensureStackTopIsCurrent();
            SectionTreeEntry currentItem = stack.peek().next();
            if (currentItem.getChildren() != null && !currentItem.getChildren().isEmpty()) {
                stack.push(currentItem.getChildren()
                                   .iterator());
            }
            return currentItem;
        }


        private void ensureStackTopIsCurrent() {

            while (!stack.isEmpty() && !stack.peek().hasNext()) {
                stack.pop();
            }
        }

    }

}
