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> targetRanges;
            List<TextElementClass> elementList;

            IndexHelper(List<TextElementClass> elementList){
                startIndex = null;
                endIndex = null;
                currentIndex = null;
                onSelectedContent = false;
                targetRanges = 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();
                    }
                }

            }

            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(){
                //this must not be called if the heading is also elected
                assert false;


                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;

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

        }

        IndexHelper indexHelper = new IndexHelper(param);

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

        this.testExpectedOutput = constructExpectedOutput(targetRanges, 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(SetUniqueList<Integer> targetIndex, ElectedText testInput){
        ListIterator<Integer> it = targetIndex.listIterator();
        List<String> testExpectedOutput = new ArrayList<>();
        while(it.hasNext()){
            testExpectedOutput.add(testInput.getStringList().get(it.next()));
        }
        return testExpectedOutput;
    }
}

@@ -1201,24 +1201,29 @@
 eger%3E target
+Index
 Ranges;%0A    
@@ -1494,24 +1494,29 @@
       target
+Index
 Ranges = new
@@ -2227,32 +2227,75 @@
              %7D%0A%0A
+                return targetIndexRanges;%0A%0A
             %7D%0A%0A 
@@ -2648,111 +2648,8 @@
 ()%7B%0A
-                //this must not be called if the heading is also elected%0A                assert false;%0A
 %0A%0A  
@@ -3604,24 +3604,29 @@
       target
+Index
 Ranges.put(s
@@ -3832,24 +3832,29 @@
 eger%3E target
+Index
 Ranges = ind
@@ -3870,24 +3870,118 @@
 process();%0A%0A
+        /*TODO: must include target section not terminated by a nominated heading%0A         */%0A
         this
@@ -4032,16 +4032,21 @@
 t(target
+Index
 Ranges, 
@@ -5245,30 +5245,29 @@
 dOutput(
-SetUniqueList%3C
+Map%3CInteger, 
 Integer%3E
@@ -5278,16 +5278,22 @@
 getIndex
+Ranges
 , Electe
@@ -5310,36 +5310,32 @@
 Input)%7B%0A        
-List
 Iterator%3CInteger
@@ -5331,39 +5331,72 @@
 tor%3C
-Integer%3E it = targetIndex.listI
+Map.Entry%3CInteger, Integer%3E%3E it = targetIndexRanges.entrySet().i
 tera
@@ -5508,70 +5508,573 @@
     
-testExpectedOutput.add(testInput.getStringList().get(it.next()
+Map.Entry%3CInteger, Integer%3E entry = it.next();%0A            int startIndex = entry.getKey();%0A            int endIndex = entry.getValue();%0A%0A            //endIndex + 1 because subList is exclusive of endIndex index argument%0A            List%3CString%3E subList = testInput.getStringList().subList(startIndex, endIndex + 1);%0A%0A            StringBuilder sb = new StringBuilder();%0A            for (String string : subList%0A                 ) %7B%0A                sb.append(string + %22 %22);%0A            %7D%0A            testExpectedOutput.add(sb.toString().replaceAll(%22%5C%5Cs+%22, %22 %22).trim(
 ));%0A

Correct output generation