package com.grayben.riskExtractor.headingMarker.markedText;

import com.grayben.riskExtractor.headingMarker.elector.ElectedText;
import org.apache.commons.collections4.list.SetUniqueList;

import java.util.*;

class MarkedTextOracle {

    private ElectedText testInput;
    private List<String> testExpectedOutput;

    protected ElectedText getTestInput(){
        return this.testInput;
    }

    protected List<String> getTestExpectedOutput(){
        return Collections.unmodifiableList(this.testExpectedOutput);
    }

    protected MarkedTextOracle(List<TextElementClass> classifiedList){
        this.testInput = generateTestInput(classifiedList);
        generateTestExpectedOutput(classifiedList, this.testInput);
    }

    protected boolean validateResult(List<String> result){
        List<String> expectedResult = getTestExpectedOutput();
        return result.equals(expectedResult);
    }

    protected void generateTestExpectedOutput(List<TextElementClass> param, ElectedText testInput) {

        class IndexHelper {
            Integer startIndex;
            Integer endIndex;
            Integer currentIndex;
            Boolean onSelectedContent;
            Map<Integer, Integer> targetIndexRanges;
            List<TextElementClass> elementList;

            IndexHelper(List<TextElementClass> elementList){
                startIndex = null;
                endIndex = null;
                currentIndex = null;
                onSelectedContent = false;
                targetIndexRanges = new HashMap<>();
                this.elementList = elementList;
            }

            Map<Integer, Integer> process(){
                ListIterator<TextElementClass> iterator
                        = elementList.listIterator();

                while (iterator.hasNext()) {
                    currentIndex = iterator.nextIndex();

                    TextElementClass elementType = iterator.next();

                    if (elementType.equals(TextElementClass.ELECTED_HEADING)) {
                        encounterElectedHeading();

                    } else if (elementType.equals(TextElementClass.NOMINATED_HEADING)) {
                        encounterNominatedHeading();
                    }
                }

                return targetIndexRanges;

            }

            void encounterElectedHeading(){
                /* if we were already in a section,
                we need to break off and start a new section */
                if(onSelectedContent){
                    completeMapEntry();
                }
                beginMapEntry();
            }

            void encounterNominatedHeading(){


                if (onSelectedContent) {
                    completeMapEntry();
                }
                /* assign onSelectedContent := false
                    -> (startIndex == null & endIndex == null) */
                assert startIndex == null && endIndex == null;
                onSelectedContent = false;
            }

            void beginMapEntry(){
                //assign(startIndex) -> startIndex was null
                assert startIndex == null;
                startIndex = currentIndex;
                onSelectedContent = true;
            }

            void completeMapEntry(){
                //if(onSelectedContent) -> startIndex is assigned
                assert startIndex != null;

                //assign(endIndex) -> endIndex was null
                assert endIndex == null;
                // index - 1 because we don't want to include the heading
                endIndex = currentIndex - 1;

                targetIndexRanges.put(startIndex, endIndex);
                startIndex = null;
                endIndex = null;
            }

        }

        IndexHelper indexHelper = new IndexHelper(param);

        Map<Integer, Integer> targetIndexRanges = indexHelper.process();

        /*TODO: must include target section not terminated by a nominated heading
         */
        this.testExpectedOutput = constructExpectedOutput(targetIndexRanges, testInput);
    }

    protected ElectedText generateTestInput(List<TextElementClass> param){

        List<String> textInput = new ArrayList<>();
        SetUniqueList<Integer> nomineeIndex
                = SetUniqueList.setUniqueList(new ArrayList<>());
        SetUniqueList<Integer> electeeIndex
                = SetUniqueList.setUniqueList(new ArrayList<>());

        ListIterator<TextElementClass> it
                = param.listIterator();

        while(it.hasNext()){
            int index;
            index = it.nextIndex();
            TextElementClass elementType = it.next();
            String stringToAdd;
            stringToAdd = index + ": " + elementType.archetype();
            textInput.add(stringToAdd);

            if(elementType.equals(TextElementClass.ELECTED_HEADING)) {
                nomineeIndex.add(index);
                electeeIndex.add(index);
            }
            else if(elementType.equals(TextElementClass.NOMINATED_HEADING)) {
                nomineeIndex.add(index);
            }
        }

        return new ElectedText(
                textInput, nomineeIndex, electeeIndex
        );
    }

    private List<String>
    constructExpectedOutput(Map<Integer, Integer> targetIndexRanges, ElectedText testInput){
        Iterator<Map.Entry<Integer, Integer>> it = targetIndexRanges.entrySet().iterator();
        List<String> testExpectedOutput = new ArrayList<>();
        while(it.hasNext()){
            Map.Entry<Integer, Integer> entry = it.next();
            int startIndex = entry.getKey();
            int endIndex = entry.getValue();

            //endIndex + 1 because subList is exclusive of endIndex index argument
            List<String> subList = testInput.getStringList().subList(startIndex, endIndex + 1);

            StringBuilder sb = new StringBuilder();
            for (String string : subList
                 ) {
                sb.append(string + " ");
            }
            testExpectedOutput.add(sb.toString().replaceAll("\\s+", " ").trim());
        }
        return testExpectedOutput;
    }
}

@@ -2227,32 +2227,404 @@
              %7D%0A%0A
+                //now, check to see if there was a non-terminated target section%0A                if(startIndex != null)%7B%0A                    //the endIndex must be null, else the section should have been closed%0A                    assert endIndex == null;%0A%0A                    currentIndex = elementList.size();%0A%0A                    completeMapEntry();%0A                %7D%0A%0A
                 

Make output generator handle target sections not terminated with candidate