'''
Created on 20-Jan-2022

@author: bthakur
'''

import inspect
import traceback
import random
import numpy as np

from xv.util.htmlutil import (
            join_with_comma, get_irrational_numbers, 
            decimal_sums_data,
            round_decimal_result, get_option_and_answer_statements,
            decimal_diff_data, decimal_prod_data, decimal_division_data,
            get_logical_inequality_data, decimal_similar_data, 
            get_random_irrational_bodmas_expr_str,
            solve_decimal_expr_str, 
                              )

from sympy import (Mul, Add,
                   )
import sympy as sp

from xv.km.util import wrap_in_latex
from xv.km.kelements import KProblemTemplate

from xv.util import UtilClass

# _irrational_operation_helper
# IrrationalOperationHelper

class IrrationalOperationHelper(UtilClass):
    
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        #self.verbose = kwargs.get('verbose', False)
        pass
    

    def _problem_template_addition(self, *args, **kwargs):
        '''
        '''
        level = 0
        irr_nums = get_irrational_numbers(level = 0,
                                    count = 2,
                                    min_max = None,
                                    exclude_range = None,
                                    distinct = True)

        nums = [round(float(x), 3) for x in irr_nums]

        correct_option = round_decimal_result(sum(nums))

        all_options = [*nums, correct_option + nums[0], correct_option + nums[-1],
                       correct_option - 2, correct_option - 1, correct_option + 1, correct_option + 2]
        all_options = [round_decimal_result(x) for x in all_options]

        incorrect_options = [x for x in all_options if x != correct_option]
        incorrect_options = sorted(incorrect_options, key = lambda x: abs(x - correct_option))

        if len(incorrect_options) > 5:
            incorrect_options = incorrect_options[:5]

        options = random.sample(incorrect_options, 3)
        options.append(correct_option)

        correct_option = wrap_in_latex(correct_option)
        options = [wrap_in_latex(x) for x in options]

        result = decimal_sums_data(nums, comments = False)

        irr_nums_latex = [f"$\\displaystyle {sp.latex(x)}$" for x in irr_nums]
        irr_nums_latex_str = " + ".join(irr_nums_latex)

        option_s, answer_s = get_option_and_answer_statements(options, correct_option)

        prob_s = f"""
        What is sum of {join_with_comma(irr_nums_latex)}?
        {option_s}
        """
        prob_s = prob_s.strip()

        soln_s = f"""
        {irr_nums_latex_str}

        Let us convert the numbers to their three decimal places:

        = {result['soln_s']}

        """

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        problemTemplate.add_problem(prob_s.strip(), "text/html")
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        return problemTemplate
        pass
    


    def _problem_template_subtraction(self, *args, **kwargs):
        '''
        '''
        level = 0
        irr_nums = get_irrational_numbers(level = 0,
                                    count = 2,
                                    min_max = None,
                                    exclude_range = None,
                                    distinct = True)

        irr_nums = list(sorted(irr_nums, reverse = True))
        nums = [round(float(x), 3) for x in irr_nums]

        correct_option = round_decimal_result(nums[0] - nums[1])

        all_options = [*nums, correct_option + nums[0], correct_option + nums[-1],
                       correct_option - 2, correct_option - 1, correct_option + 1, correct_option + 2]
        all_options = [round_decimal_result(x) for x in all_options]

        incorrect_options = [x for x in all_options if x != correct_option]
        incorrect_options = sorted(incorrect_options, key = lambda x: abs(x - correct_option))

        if len(incorrect_options) > 5:
            incorrect_options = incorrect_options[:5]

        options = random.sample(incorrect_options, 3)
        options.append(correct_option)

        correct_option = wrap_in_latex(correct_option)
        options = [wrap_in_latex(x) for x in options]

        result = decimal_diff_data(nums, comments = False)

        irr_nums_latex = [f"$\\displaystyle {sp.latex(x)}$" for x in irr_nums]
        irr_nums_latex_str = " - ".join(irr_nums_latex)

        option_s, answer_s = get_option_and_answer_statements(options, correct_option)

        prob_s = f"""
        What is difference between {join_with_comma(irr_nums_latex)}?
        {option_s}
        """
        prob_s = prob_s.strip()

        soln_s = f"""
        {irr_nums_latex_str}

        Let us convert the numbers to their three decimal places:

        ={result['soln_s']}

        """

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        problemTemplate.add_problem(prob_s.strip(), "text/html")
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        return problemTemplate
        pass
    


    def _problem_template_multiplication(self, *args, **kwargs):
        '''
        '''
        level = 0
        irr_nums = get_irrational_numbers(level = 0,
                                    count = 2,
                                    min_max = None,
                                    exclude_range = None,
                                    distinct = True)

        nums = [round(float(x), 2) for x in irr_nums]

        correct_option = round_decimal_result(Mul(*nums))

        all_options = [*nums, correct_option + nums[0], correct_option + nums[-1],
                       correct_option - 2, correct_option - 1, correct_option + 1, correct_option + 2]
        all_options = [round_decimal_result(x) for x in all_options]
        incorrect_options = [x for x in all_options if x != correct_option]
        incorrect_options = sorted(incorrect_options, key = lambda x: abs(x - correct_option))

        if len(incorrect_options) > 5:
            incorrect_options = incorrect_options[:5]

        options = random.sample(incorrect_options, 3)
        options.append(correct_option)

        correct_option = wrap_in_latex(correct_option)
        options = [wrap_in_latex(x) for x in options]

        result = decimal_prod_data(nums, comments = False)

        irr_nums_latex = [f"$\\displaystyle {sp.latex(x)}$" for x in irr_nums]
        irr_nums_latex_str = " * ".join(irr_nums_latex)

        option_s, answer_s = get_option_and_answer_statements(options, correct_option)


        prob_s = f"""
        What is product of {join_with_comma(irr_nums_latex)}?
        {option_s}
        """
        prob_s = prob_s.strip()

        soln_s = f"""
        {irr_nums_latex_str}

        Let us convert the numbers to their two decimal places:

        = {result['soln_s']}

        """

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        problemTemplate.add_problem(prob_s.strip(), "text/html")
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        return problemTemplate
        pass
    


    def _problem_template_division(self, *args, **kwargs):
        '''
        '''

        def Divide(*lst):
            return Mul(lst[0], lst[1])
            pass

        level = 0
        irr_nums = get_irrational_numbers(level = 0,
                                    count = 2,
                                    min_max = None,
                                    exclude_range = None,
                                    distinct = True)

        nums = [round(float(x), 2) for x in irr_nums]

        result = decimal_division_data(nums, comments = False)

        nums_result = result['nums_result']
        correct_option = round_decimal_result(eval(f"{nums_result}"))

        all_options = [*nums, correct_option + nums[0], correct_option + nums[-1],
                       correct_option - 2, correct_option - 1, correct_option + 1, correct_option + 2]
        all_options = [round_decimal_result(x) for x in all_options]

        incorrect_options = [x for x in all_options if x != correct_option]
        incorrect_options = sorted(incorrect_options, key = lambda x: abs(x - correct_option))

        if len(incorrect_options) > 5:
            incorrect_options = incorrect_options[:5]

        options = random.sample(incorrect_options, 3)
        options.append(correct_option)

        correct_option = wrap_in_latex(correct_option)
        options = [wrap_in_latex(x) for x in options]

        irr_nums_latex = [f"$\\displaystyle {sp.latex(x)}$" for x in irr_nums]
        irr_nums_latex_str = f" $\\div $ ".join(irr_nums_latex)

        option_s, answer_s = get_option_and_answer_statements(options, correct_option)

        prob_s = f"""
        Divide {irr_nums_latex[0]} by {irr_nums_latex[1]}
        {option_s}
        """
        prob_s = prob_s.strip()

        soln_s = f"""
        {irr_nums_latex_str}

        Let us convert the numbers to their two decimal places:

        = {result['soln_s']}

        """

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        problemTemplate.add_problem(prob_s.strip(), "text/html")
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        return problemTemplate
        pass
    


    def _problem_template_additional_logical(self, *args, **kwargs):
        '''
        '''
        level = 0

        while True:
            irr_nums_set1 = get_irrational_numbers(level = 0,
                                        count = 2,
                                        min_max = None,
                                        exclude_range = None,
                                        distinct = True)

            nums_set1 = [round(float(x), 3) for x in irr_nums_set1]
            if nums_set1[0] == nums_set1[1]:
                continue

            irr_nums_set2 = get_irrational_numbers(level = 0,
                                        count = 2,
                                        min_max = None,
                                        exclude_range = None,
                                        distinct = True)

            nums_set2 = [round(float(x), 3) for x in irr_nums_set2]

            nums_rev = list(reversed(nums_set1))
            nums_set2 = random.choice([nums_rev, nums_set2])
            if (nums_set1[0] == nums_set2[0] and
                nums_set1[1] == nums_set2[1]):
                continue
            break
            pass

        logical_result = get_logical_inequality_data(sum(nums_set1), sum(nums_set2))

        correct_option = logical_result['correct_option']
        options = logical_result['options']

        result1 = decimal_sums_data(nums_set1, comments = False)
        result2 = decimal_sums_data(nums_set2, comments = False)

        similar_sums = decimal_similar_data([result1['sum'], result2['sum']])

        irr_nums_set1_latex = [f"$\\displaystyle {sp.latex(x)}$" for x in irr_nums_set1]
        irr_nums_set1_latex_str = " + ".join(irr_nums_set1_latex)

        irr_nums_set2_latex = [f"$\\displaystyle {sp.latex(x)}$" for x in irr_nums_set2]
        irr_nums_set2_latex_str = " + ".join(irr_nums_set2_latex)


        option_s, answer_s = get_option_and_answer_statements(options, correct_option)

        prob_s = f"""
        {irr_nums_set1_latex_str} ___________ {irr_nums_set2_latex_str}
        {option_s}
        """
        prob_s = prob_s.strip()

        soln_s = f"""
        <h3>Left hand side:</h3>
        {irr_nums_set1_latex_str}

        = {result1['soln_s']}
        <h3>Right hand side:</h3>
        {irr_nums_set2_latex_str}

        = {result2['soln_s']}

        <h3>Make the LHS and RHS sums similar to comapre them:</h3>
        $\\displaystyle \\text{{ LHS }} = {similar_sums['similar_nums'][0]} $
        $\\displaystyle \\text{{ RHS }} = {similar_sums['similar_nums'][1]} $

        $\\displaystyle \\implies $ LHS {correct_option} RHS
        """

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        problemTemplate.add_problem(prob_s.strip(), "text/html")
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        return problemTemplate
        pass
    


    def _problem_template_expr(self, *args, **kwargs):
        '''
        '''
        level = 0
        while True:
            count = random.randint(3, 5)
            irr_nums = get_irrational_numbers(level = 0,
                                        count = count,
                                        max_digits = 4,
                                        exclude_range = None,
                                        distinct = True)

            bodmas_result = get_random_irrational_bodmas_expr_str(irr_nums, precision = 2)
            expr_str = bodmas_result['expr_str']
            expr_ops = bodmas_result['expr_ops']

            latex_expr_str = bodmas_result['latex_expr_str']

            decimal_expr_str = bodmas_result['decimal_expr_str']
            irr_nums = bodmas_result['irr_nums']
            nums = bodmas_result['nums']

            result = solve_decimal_expr_str(decimal_expr_str)

            expr_value = result['expr_value']

            correct_option = expr_value

            all_options = [*nums,
                           correct_option + nums[0], correct_option + nums[-1],
                           correct_option - nums[0], correct_option - nums[-1],
                           correct_option - 2, correct_option - 1, correct_option + 1, correct_option + 2]
            all_options = [round_decimal_result(x) for x in all_options]
            incorrect_options = [x for x in list(set(all_options)) if x != correct_option]
            incorrect_options = sorted(incorrect_options, key = lambda x: abs(x - correct_option))

            if len(incorrect_options) >= 3:
                break
            pass

        if len(incorrect_options) > 5:
            incorrect_options = incorrect_options[:5]

        options = random.sample(incorrect_options, 3)
        options.append(correct_option)

        options = [f"$\\displaystyle {sp.latex(x)}$" for x in options]
        correct_option = f"$\\displaystyle {sp.latex(correct_option)}$"

        option_s, answer_s = get_option_and_answer_statements(options, correct_option)

        prob_s = f"""
        Solve {latex_expr_str}
        {option_s}
        """
        prob_s = prob_s.strip()

        soln_s = f"""
        {latex_expr_str}

        Let us convert the numbers to their two decimal places:

        = {result['soln_s']}
        """

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        problemTemplate.add_problem(prob_s.strip(), "text/html")
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        return problemTemplate
        pass
    


    def _problem_template_expr_logical(self, *args, **kwargs):
        '''
        '''

        level = 0
        while True:
            count = random.randint(3, 5)
            irr_nums_set1 = get_irrational_numbers(level = 0,
                                        count = count,
                                        max_digits = 4,
                                        exclude_range = None,
                                        distinct = True)

            bodmas_result1 = get_random_irrational_bodmas_expr_str(irr_nums_set1, precision = 2)
            latex_expr1_str = bodmas_result1['latex_expr_str']
            decimal_expr1_str = bodmas_result1['decimal_expr_str']
            nums_set1 = bodmas_result1['nums']


            count = random.randint(3, 5)
            irr_nums_set2 = get_irrational_numbers(level = 0,
                                        count = count,
                                        max_digits = 4,
                                        exclude_range = None,
                                        distinct = True)

            bodmas_result2 = get_random_irrational_bodmas_expr_str(irr_nums_set2, precision = 2)
            latex_expr2_str = bodmas_result2['latex_expr_str']
            decimal_expr2_str = bodmas_result2['decimal_expr_str']
            nums_set2 = bodmas_result2['nums']

            result1 = solve_decimal_expr_str(decimal_expr1_str)
            result2 = solve_decimal_expr_str(decimal_expr2_str)

            expr1_value = result1['expr_value']
            expr2_value = result2['expr_value']

            break
            pass

        ops_data = [
            ["==", "is equal to"],
            ["<", "is less than"],
            [">", "is more than"],
            ["", "can't say"],
        ]

        correct_option = None
        for op, op_text in ops_data[:-1]:
            if eval(f"{expr1_value} {op} {expr2_value}"):
                correct_option = op_text
                pass
            pass
        if correct_option is None:
            correct_option = ops_data[-1][1]

        options = [x[1] for x in ops_data]

        option_s, answer_s = get_option_and_answer_statements(options, correct_option)

        prob_s = f"""
        {latex_expr1_str} ___________ {latex_expr2_str}
        {option_s}
        """
        prob_s = prob_s.strip()

        soln_s = f"""
        <h3>Left Hand Side </h3>
        {latex_expr1_str}

        = {result1['soln_s']}

        <h3>Right Hand Side </h3>
        {latex_expr2_str}

        = {result2['soln_s']}

        <h3>Therefore,</h3>
        LHS {correct_option} RHS
        """

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        problemTemplate.add_problem(prob_s.strip(), "text/html")
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        return problemTemplate
        pass
    

    pass
#class IrrationalOperationHelper:
