package com.amee.service.data;

import com.amee.domain.IDataCategoryReference;
import com.amee.domain.cache.CacheHelper;
import com.amee.domain.data.DataCategory;
import com.amee.domain.data.ItemDefinition;
import com.amee.domain.sheet.Choice;
import com.amee.domain.sheet.Choices;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

@Service
public class DrillDownService {

    @Autowired
    private DataServiceDAO dataServiceDao;

    @Autowired
    private DrillDownDAO drillDownDao;

    private CacheHelper cacheHelper = CacheHelper.getInstance();

    public Choices getChoices(IDataCategoryReference dc, List<Choice> selections) {

        // Get Data Category.
        DataCategory dataCategory = dataServiceDao.getDataCategory(dc);

        // we can do a drill down if an itemDefinition is attached to current dataCategory
        ItemDefinition itemDefinition = dataCategory.getItemDefinition();
        List<Choice> drillDownChoices = null;
        List<Choice> values = new ArrayList<Choice>();
        if (itemDefinition != null) {

            // obtain drill down choices
            drillDownChoices = itemDefinition.getDrillDownChoices();

            // fix-up selections and drill downs
            matchSelectionOrderToDrillDownChoices(drillDownChoices, selections);
            removeSelectionsNotInDrillDownChoices(drillDownChoices, selections);
            removeDrillDownChoicesThatHaveBeenSelected(drillDownChoices, selections);
            removeSelectionsWithNullValues(selections);

            // get drill down values
            values = getDataItemChoices(dataCategory, selections, drillDownChoices);
        }

        // work out name
        String name;
        if ((drillDownChoices != null) && (drillDownChoices.size() > 0)) {
            name = drillDownChoices.get(0).getName();
        } else {
            name = "uid";
        }

        // We either have only one value apart from uid or we have no values for this drilldown selection.
        if (!name.equals("uid") && (values.size() <= 1)) {
            if (!values.isEmpty()) {

                // skip ahead if we only have one value that is not "uid"
                selections.add(new Choice(name, values.get(0).getValue()));
            } else {

                // Set this value to null. It will be removed from the selections in removeSelectionsWithNullValues.
                selections.add(new Choice(name, null));
            }
            return getChoices(dataCategory, selections);
        } else {
            // wrap result in Choices object
            return new Choices(name, values);
        }
    }

    public void clearDrillDownCache() {
        cacheHelper.clearCache("DrillDownChoices");
    }

    @SuppressWarnings("unchecked")
    protected List<Choice> getDataItemChoices(
            IDataCategoryReference dataCategory,
            List<Choice> selections,
            List<Choice> drillDownChoices) {
        // Get Choices and sort.
        List<Choice> choices = ((List<Choice>) cacheHelper.getCacheable(
                new DrillDownFactory(drillDownDao, dataCategory, selections, drillDownChoices)));
        Collections.sort(choices);
        return choices;
    }

    private void matchSelectionOrderToDrillDownChoices(List<Choice> drillDownChoices, List<Choice> selections) {
        for (Choice c : drillDownChoices) {
            int selectionIndex = selections.indexOf(c);
            if (selectionIndex >= 0) {
                selections.add(selections.remove(selectionIndex));
            }
        }
    }

    private void removeDrillDownChoicesThatHaveBeenSelected(List<Choice> drillDownChoices, List<Choice> selections) {
        Iterator<Choice> iterator;
        Choice choice;
        iterator = drillDownChoices.iterator();
        while (iterator.hasNext()) {
            choice = iterator.next();
            if (selections.contains(choice)) {
                iterator.remove();
            }
        }
    }

    private void removeSelectionsNotInDrillDownChoices(List<Choice> drillDownChoices, List<Choice> selections) {
        Iterator<Choice> iterator;
        Choice choice;
        iterator = selections.iterator();
        while (iterator.hasNext()) {
            choice = iterator.next();
            if (!drillDownChoices.contains(choice)) {
                iterator.remove();
            }
        }
    }
    
    private void removeSelectionsWithNullValues(List<Choice> selections) {
        Iterator<Choice> iterator;
        Choice choice;
        iterator = selections.iterator();
        while (iterator.hasNext()) {
            choice = iterator.next();
            if (choice.getValue() == null) {
                iterator.remove();
            }
        }
    }
}
@@ -2052,173 +2052,151 @@
  // 
-We either have only one value apart from uid or we have no values for this drilldown selection.%0A        if (!name.equals(%22uid%22) && (values.size() %3C= 1)) %7B%0A  
+If this drilldown has no value, set it to null.%0A        // It will be removed from the selections in removeSelectionsWithNullValues().%0A
         
   if
@@ -2191,23 +2191,20 @@
         
-  
 if (
-!
 values.i
@@ -2211,29 +2211,24 @@
 sEmpty()) %7B%0A
-%0A    
             
@@ -2231,285 +2231,242 @@
     
-// skip ahead if we only have one value that is not %22uid%22%0A                selections.add(new Choice(name, values.get(0).getValue()));%0A            %7D else %7B%0A%0A                // Set this value to null. It will be removed from the selections in removeSelectionsWithNullValues.%0A    
+selections.add(new Choice(name, null));%0A            return getChoices(dataCategory, selections);%0A        %7D else if (!name.equals(%22uid%22) && (values.size() == 1)) %7B%0A%0A            // skip ahead if we only have one value that is not %22uid%22%0A
     
@@ -2509,29 +2509,35 @@
 me, 
-null));%0A            %7D
+values.get(0).getValue()));
 %0A   

Refactor getChoices. PL-11188.