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);

            // 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";
        }

        // skip ahead if we only have one value that is not "uid"
        if (!name.equals("uid") && (values.size() == 1)) {
            selections.add(new Choice(name, values.get(0).getValue()));
            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;
    }

    protected 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));
            }
        }
    }

    protected 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();
            }
        }
    }

    protected 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();
            }
        }
    }
}
@@ -1617,16 +1617,72 @@
 ctions);
+%0A            removeSelectionsWithNullValues(selections);
 %0A%0A      
@@ -2052,62 +2052,103 @@
  // 
-skip ahead if we only
+We either have only one value apart from uid or we
  have 
+n
 o
-ne
  value
- that is not %22uid%22
+s for this drilldown selection.
 %0A   
@@ -2194,17 +2194,17 @@
 .size() 
-=
+%3C
 = 1)) %7B%0A
@@ -2219,67 +2219,392 @@
     
-selections.add(new Choice(name, values.get(0).getValue()));
+if (!values.isEmpty()) %7B%0A%0A                // 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            %7D
 %0A   
@@ -3366,31 +3366,29 @@
    %7D%0A%0A    pr
-otec
+iva
 te
-d
  void matchS
@@ -3712,39 +3712,37 @@
  %7D%0A    %7D%0A%0A    pr
-otec
+iva
 te
-d
  void removeDril
@@ -4132,23 +4132,21 @@
 %0A%0A    pr
-otec
+iva
 te
-d
  void re
@@ -4533,9 +4533,374 @@
 %7D%0A    %7D%0A
+    %0A    private void removeSelectionsWithNullValues(List%3CChoice%3E selections) %7B%0A        Iterator%3CChoice%3E iterator;%0A        Choice choice;%0A        iterator = selections.iterator();%0A        while (iterator.hasNext()) %7B%0A            choice = iterator.next();%0A            if (choice.getValue() == null) %7B%0A                iterator.remove();%0A            %7D%0A        %7D%0A    %7D%0A
 %7D

Handle drilldowns that have no value. PL-11188.