from .models import Question, Answer
import logging


def explode_answer_into_list(answers):
    answer_list = []

    for answer in answers:
        if type(answer) == type(list()):
            string_list = filter(lambda v: not isinstance(v, int), answer)
            # remove all number values from list - this means choice-multiple-values's values
            answer_list.extend(string_list)
        else:
            answer_list.append(answer)

    return answer_list


def check_actual_answers_against_expression(check_answer, actual_answer, check_question):
    # Numeric Value Expressions
    if check_answer[0:1] in "<>":
        try:
            actual_answer = float(actual_answer)
            if check_answer[1:2] == "=":
                check_value = float(check_answer[2:])
            else:
                check_value = float(check_answer[1:])
        except:
            logging.error("ERROR: must use numeric values with < <= => > checks (%r)" % check_question)
            return False
        if check_answer.startswith("<="):
            return actual_answer <= check_value
        if check_answer.startswith(">="):
            return actual_answer >= check_value
        if check_answer.startswith("<"):
            return actual_answer < check_value
        if check_answer.startswith(">"):
            return actual_answer > check_value

    # Convert answer to list if not already one
    if type(actual_answer) != type(list()):
        actual_answer = [actual_answer]

    # Negative Value Expressions
    if check_answer.startswith("!"):
        if len(actual_answer) == 0:
            return False
        for actual_answer in actual_answer:
            if actual_answer == '':
                return False
            if check_answer[1:].strip() == actual_answer.strip():
                return False
        return True

    # Positive Value Expressions
    for actual_answer in actual_answer:
        if check_answer.strip() == actual_answer.strip():
            return True
    return False


def dep_check(expr, runinfo, answerdict):
    """
    Given a comma separated question number and expression, determine if the
    provided answer to the question number satisfies the expression.

    If the expression starts with >, >=, <, or <=, compare the rest of
    the expression numerically and return False if it's not able to be
    converted to an integer.

    If the expression starts with !, return true if the rest of the expression
    does not match the answer.

    Otherwise return true if the expression matches the answer.

    If there is no comma and only a question number, it checks if the answer
    is "yes"

    When looking up the answer, it first checks if it's in the answerdict,
    then it checks runinfo's cookies, then it does a database lookup to find
    the answer.

    The use of the comma separator is purely historical.
    """

    if hasattr(runinfo, 'questionset'):
        questionnaire = runinfo.questionset.questionnaire
    elif hasattr(runinfo, 'questionnaire'):
        questionnaire = runinfo.questionnaire
    else:
        assert False

    # Parse expression
    if "," not in expr:
        expr += ",yes"

    check_question_number, check_answer = expr.split(",", 1)

    # Get question to check
    try:
        check_question = Question.objects.get(number=check_question_number,
                                              questionset__questionnaire=questionnaire)
    except Question.DoesNotExist:
        return False

    # Parse & load actual answer(s) from user
    if check_question in answerdict:
        # test for membership in multiple choice questions
        # FIXME: only checking answerdict
        for k, v in answerdict[check_question].items():
            if not k.startswith('multiple_'):
                continue
            if check_answer.startswith("!"):
                if check_answer[1:].strip() == v.strip():
                    return False
            elif check_answer.strip() == v.strip():
                return True
        actual_answer = answerdict[check_question].get('ANSWER', '')
    elif hasattr(runinfo, 'get_cookie') and runinfo.get_cookie(check_question_number, False):
        actual_answer = runinfo.get_cookie(check_question_number)
    else:
        # retrieve from database
        answer_object = Answer.objects.filter(question=check_question,
                                              run=runinfo.run,
                                              subject=runinfo.subject)
        if answer_object:
            actual_answer = answer_object[0].split_answer()
            logging.warn("Put `store` in checks field for question %s" % check_question_number)
        else:
            actual_answer = None

    if not actual_answer:
        if check_question.getcheckdict():
            actual_answer = check_question.getcheckdict().get('default')

    if actual_answer is None:
        actual_answer = u''

    # Convert freeform question type answers from a list of lists to just a single list.
    # FIXME: Figure out why the hell freeform questions store their values as lists within lists.
    #Don't shrink to one list if it isn't even a list though, otherwise we could get a string 
    #becoming a character array and falsely failing the comparison.
    answer_list = actual_answer #might not actually be a list but meh
    if type(actual_answer) == type(list()):
        answer_list = explode_answer_into_list(actual_answer)

    return check_actual_answers_against_expression(check_answer, answer_list, check_question)
