package com.noprestige.kanaquiz.questions;

import com.noprestige.kanaquiz.R;
import com.noprestige.kanaquiz.logs.LogDao;
import com.noprestige.kanaquiz.options.OptionsControl;

import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

public class KanaQuestionBank extends WeightedList<KanaQuestion>
{
    private KanaQuestion currentQuestion;
    private Set<String> fullAnswerList = new TreeSet<>(new GojuonOrder());
    private Map<String, WeightedList<String>> weightedAnswerListCache = new TreeMap<>();

    private static final int MAX_MULTIPLE_CHOICE_ANSWERS = 6;

    private QuestionRecord previousQuestions;
    private int maxAnswerWeight = -1;

    KanaQuestionBank() {}

    public void newQuestion() throws NoQuestionsException
    {
        if (this.count() > 0)
        {
            if (previousQuestions == null)
                previousQuestions =
                        new QuestionRecord(Math.min(this.count(), OptionsControl.getInt(R.string.prefid_repetition)));
            do
                currentQuestion = this.getRandom();
            while (!previousQuestions.add(currentQuestion));
        }
        else
            throw new NoQuestionsException();
    }

    public String getCurrentKana()
    {
        return currentQuestion.getKana();
    }

    public boolean checkCurrentAnswer(String response)
    {
        return currentQuestion.checkAnswer(response);
    }

    public String fetchCorrectAnswer()
    {
        return currentQuestion.fetchCorrectAnswer();
    }

    public boolean addQuestions(KanaQuestion[] questions)
    {
        previousQuestions = null;
        maxAnswerWeight = -1;
        if (questions != null)
        {
            boolean returnValue = true;
            for (KanaQuestion question : questions)
            {
                // Fetches the percentage of times the user got a kana right,
                Float percentage = LogDao.getKanaPercentage(question.getKana());
                if (percentage == null)
                    percentage = 0.1f;
                // The 1f is to invert the value so we get the number of times they got it wrong,
                // Times 100f to get the percentage.
                int weight = (int) Math.ceil((1f - percentage) * 100f);
                // Setting weight to never get lower than 2,
                // so any kana the user got perfect will still appear in the quiz.
                if (weight < 2)
                    weight = 2;
                // if any one of the additions fail, the method returns false
                returnValue =
                        this.add(weight, question) && fullAnswerList.add(question.fetchCorrectAnswer()) && returnValue;
            }
            return returnValue;
        }
        return false;
    }

    public boolean addQuestions(KanaQuestionBank questions)
    {
        previousQuestions = null;
        maxAnswerWeight = -1;
        this.fullAnswerList.addAll(questions.fullAnswerList);
        return this.merge(questions);
    }

    private int getMaxAnswerWeight()
    {
        if (maxAnswerWeight < 0)
        {
            // Max value is to prevent integer overflow in the weighted answer list. Number is chosen so if every
            // answer had this count (which is actually impossible because of the range restriction, but better to
            // err on the side of caution) the resultant calculations of 2^count would not add up to an integer
            // overflow.
            maxAnswerWeight = (int) Math.floor(Math.log(Integer.MAX_VALUE / (fullAnswerList.size() - 1)) / Math.log(2));
        }
        return maxAnswerWeight;
    }

    public String[] getPossibleAnswers()
    {
        return getPossibleAnswers(MAX_MULTIPLE_CHOICE_ANSWERS);
    }

    public String[] getPossibleAnswers(int maxChoices)
    {
        if (fullAnswerList.size() <= maxChoices)
        {
            String[] answerArray = new String[fullAnswerList.size()];
            fullAnswerList.toArray(answerArray);
            return answerArray;
        }
        else
        {
            if (!weightedAnswerListCache.containsKey(getCurrentKana()))
            {
                int maxCount = 0;
                int minCount = Integer.MAX_VALUE;
                Map<String, Float> answerCounts = new TreeMap<>();
                for (String answer : fullAnswerList)
                {
                    if (!answer.equals(fetchCorrectAnswer()))
                    {
                        //fetch all data
                        int count = LogDao.getIncorrectAnswerCount(getCurrentKana(), answer);
                        maxCount = Math.max(maxCount, count);
                        minCount = Math.min(minCount, count);
                        answerCounts.put(answer, (float) count);
                    }
                }

                // Because of the inherent properties of the power function used later, subtracting the same amount
                // from each count so the lowest count is zero will not change the relative weight of each item. And
                // doing so will also save the space I need to prevent overflow issues.
                if (minCount > 0)
                {
                    maxCount -= minCount;
                    for (String answer : answerCounts.keySet())
                    {
                        float newCount = answerCounts.remove(answer) - minCount;
                        answerCounts.put(answer, newCount);
                    }
                }

                if (maxCount > getMaxAnswerWeight())
                {
                    float controlFactor = getMaxAnswerWeight() / maxCount;
                    for (String answer : answerCounts.keySet())
                    {
                        float newCount = answerCounts.remove(answer) * controlFactor;
                        answerCounts.put(answer, newCount);
                    }
                }

                WeightedList<String> weightedAnswerList = new WeightedList<>();
                for (String answer : answerCounts.keySet())
                    weightedAnswerList.add(Math.pow(2, answerCounts.get(answer)), answer);

                weightedAnswerListCache.put(getCurrentKana(), weightedAnswerList);
            }

            TreeSet<String> possibleAnswerStrings = new TreeSet<>(new GojuonOrder());

            possibleAnswerStrings.add(fetchCorrectAnswer());

            WeightedList<String> weightedAnswerList = weightedAnswerListCache.get(getCurrentKana());

            while (possibleAnswerStrings.size() < maxChoices)
                possibleAnswerStrings.add(weightedAnswerList.getRandom());

            String[] returnValue = new String[possibleAnswerStrings.size()];
            possibleAnswerStrings.toArray(returnValue);
            return returnValue;
        }
    }
}

@@ -521,36 +521,18 @@
 istCache
- = new TreeMap%3C%3E()
 ;%0A
+
 %0A    pri
@@ -1578,32 +1578,72 @@
 uestions)%0A    %7B%0A
+        weightedAnswerListCache = null;%0A
         previous
@@ -2877,32 +2877,72 @@
 uestions)%0A    %7B%0A
+        weightedAnswerListCache = null;%0A
         previous
@@ -4122,32 +4122,32 @@
  %7D%0A        else%0A
-
         %7B%0A      
@@ -4132,32 +4132,140 @@
  else%0A        %7B%0A
+            if (weightedAnswerListCache == null)%0A                weightedAnswerListCache = new TreeMap%3C%3E();%0A
             if (

Set weightedAnswerListCache to reset on change