'''
Created on 02-Mar-2021

@author: bthakur
'''
import numpy as np
import random
import inspect
import traceback
from sympy import simplify, sympify, latex, Rational, Symbol, symbols

from xv.km.util import latex_fraction
        
        
from ..helper import BaseHelper
from xv.km.managers import BaseManager
from xv.km.kelements import KProblemTemplate
from xv.util import list_to_str, toIntOrFloat

class PositionalValueManager(BaseManager):
    '''
    Number base and positional values, conversion of numbers between bases

    Minimum Grade: 3
    Maximum Grade: 12


    Examples
    --------
        ke = PositionalValueManager()
    
        ke.printProblemTypes()
        ke.helpProblemType(0)
    
        ke.getRandomProblem(problem_type = 0)
        ke.getRandomProblem(problem_type = 1)
        ...
        ke.printProblem()
        ke.printAnswer()
        ke.printSolution()


    doc_style: xv_doc
    ''' 
    
    def __new__(cls, *args, **kwargs):
        instance = super(PositionalValueManager, cls).__new__(cls, *args, **kwargs)
        for base_class in PositionalValueManager.__bases__:
            base_class.__init__(instance, *args, **kwargs)
        return instance

    def __init__(self, *args, **kwargs):
        self.base_helper = BaseHelper(**kwargs)
        pass


    def _init_problem_types(self, *args, **kwargs):
        self._append_problem_types([
            '_problem_expand_number_base_10',
            '_problem_expand_number_with_decimal_base_10',
            '_problem_expand_number_base_variable',
            '_problem_expand_number_with_decimal_base_variable',
            '_problem_numbers_on_base_n_to_base_10',
            '_problem_conver_base_10_to_base_n',
            '_problem_recurring_base_n_decimal_to_base_10',
            '_problem_express_n_digit_number_as_algebraic_expr',
            '_problem_express_number_with_decimal_as_algebraic_expr',
            '_problem_derive_algebraic_expr_two_digit',
            '_problem_multi_digit_numbers',
         ])
        pass

    def _problem_expand_number_base_10(self, *args, **kwargs):
        """
        _problem_expand_number_base_10

        Minimum Grade: 3
        Maximum Grade: 12

        doc_style: xv_doc
        """
        pvhelper = self.base_helper.get_arithmetic_helper().get_positional_value_helper()
        result = pvhelper.get_random_positional_values()

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Expand {result['num']} with positional values of its digits.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        {result['num']}
        
        =$\\displaystyle {result['pos_values_latex_str']}$
        
        = {result['pos_values_str']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['pos_values_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    def _problem_expand_number_with_decimal_base_10(self, *args, **kwargs):
        """
        _problem_expand_number_with_decimal_base_10

        Minimum Grade: 3
        Maximum Grade: 12

        doc_style: xv_doc
        """
        pvhelper = self.base_helper.get_arithmetic_helper().get_positional_value_helper()
        
        decimal_digits = random.randint(0, 4)
        result = pvhelper.get_random_positional_values(decimal_digits = decimal_digits)

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Expand {result['num']} with positional values of its digits.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        {result['num']}
        
        =$\\displaystyle {result['pos_values_latex_str']}$
        
        = $\\displaystyle {result['pos_values_str']}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        = $\\displaystyle {result['pos_values_str']}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    
    def _problem_expand_number_base_variable(self, *args, **kwargs):
        """
        _problem_expand_number_base_variable

        Minimum Grade: 3
        Maximum Grade: 12

        doc_style: xv_doc
        """
        pvhelper = self.base_helper.get_arithmetic_helper().get_positional_value_helper()
        
        n_base = random.choice([2, 3, 4, 5, 6, 7, 8, 9, 16])
        
        result = pvhelper.get_random_positional_values(n_base = n_base)

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Convert {result['num']} on base {result['n_base']} to base 10.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        {result['num']} on base {result['n_base']}
        
        = $\\displaystyle 
        {result['num_latex']}
        $ 
        
        = $\\displaystyle {result['pos_values_latex_str']}$
        
        = $\\displaystyle {result['pos_values_str']}$
        
        = $\\displaystyle {result['num_base10_latex']}$
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {result['num_base10_latex']}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    def _problem_expand_number_with_decimal_base_variable(self, *args, **kwargs):
        """
        _problem_expand_number_with_decimal_base_variable

        Minimum Grade: 3
        Maximum Grade: 12

        doc_style: xv_doc
        """
        pvhelper = self.base_helper.get_arithmetic_helper().get_positional_value_helper()
        
        n_base = random.choice([2, 3, 4, 5, 6, 7, 8, 9, 16])
        
        decimal_digits = random.randint(0, 4)
        result = pvhelper.get_random_positional_values(n_base = n_base,
                                                       decimal_digits = decimal_digits)

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Convert {result['num']} on base {result['n_base']} to base 10.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        {result['num']} on base {result['n_base']}
        
        = $\\displaystyle 
        {result['num_latex']}
        $ 
        
        = $\\displaystyle {result['pos_values_latex_str']}$
        
        = $\\displaystyle {result['pos_values_str']}$
        
        = $\\displaystyle {result['num_base10_latex']}$
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {result['num_base10_latex']}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    def _problem_numbers_on_base_n_to_base_10(self, *args, **kwargs):
        """

        Minimum Grade: 3
        Maximum Grade: 12

        doc_style: xv_doc
        """
        pvhelper = self.base_helper.get_arithmetic_helper().get_positional_value_helper()
        
        n_base = random.choice([2, 3, 4, 5, 6, 7, 8, 9, 16])
        
        n_base = random.choice([n_base, 10])
        
        decimal_digits = random.randint(2, 5)
        result = pvhelper.get_recurring_decimals(n_base = n_base,
                                                 decimal_digits = decimal_digits)

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Convert $\\displaystyle {result['num_latex']}$ to a rational number on base 10.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        {result['soln_s']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {result['num_base10_latex']}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    

    def _problem_conver_base_10_to_base_n(self, *args, **kwargs):
        """

        Minimum Grade: 3
        Maximum Grade: 12

        doc_style: xv_doc
        """
        pvhelper = self.base_helper.get_arithmetic_helper().get_positional_value_helper()
        
        
        result = pvhelper.get_problem_conver_base_10_to_base_n(**kwargs)

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        prob_s = f"""
        {result['prob_s']}
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        {result['soln_s']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['answer_s']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    

    def _problem_recurring_base_n_decimal_to_base_10(self, *args, **kwargs):
        """

        Minimum Grade: 3
        Maximum Grade: 12

        doc_style: xv_doc
        """
        pvhelper = self.base_helper.get_arithmetic_helper().get_positional_value_helper()
        
        n_base = random.choice([2, 3, 4, 5, 6, 7, 8, 9, 16])
        
        n_base = random.choice([n_base, 10])
        
        decimal_digits = random.randint(2, 5)
        result = pvhelper.get_recurring_decimals(n_base = n_base,
                                                 decimal_digits = decimal_digits)

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Convert $\\displaystyle {result['num_latex']}$ to a rational number on base 10.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        {result['soln_s']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {result['num_base10_latex']}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    
    def _problem_express_n_digit_number_as_algebraic_expr(self, *args, **kwargs):
        """
        _problem_express_n_digit_number_as_algebraic_expr

        Minimum Grade: 3
        Maximum Grade: 12

        doc_style: xv_doc
        """
        pvhelper = self.base_helper.get_arithmetic_helper().get_positional_value_helper()
        
        n_base = random.choice([2, 3, 4, 5, 6, 7, 8, 9, 10, 16])
        
        result = pvhelper.get_n_digit_number_as_algebraic_expr(n_base = n_base)

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        A number on base {result['n_base']} is written as {result['num']}.
        Express it as an algebraic expression.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        {result['num']} on base {result['n_base']}
        
        = $\\displaystyle 
        {result['num_latex']}
        $ 
        
        = $\\displaystyle {result['pos_values_latex_str']}$
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {result['pos_values_latex_str']}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    def _problem_express_number_with_decimal_as_algebraic_expr(self, *args, **kwargs):
        """
        _problem_express_number_with_decimal_as_algebraic_expr

        Minimum Grade: 3
        Maximum Grade: 12

        doc_style: xv_doc
        """
        pvhelper = self.base_helper.get_arithmetic_helper().get_positional_value_helper()
        
        n_base = random.choice([2, 3, 4, 5, 6, 7, 8, 9, 10, 16])
        
        decimal_digits = random.randint(0, 4)
        result = pvhelper.get_n_digit_number_as_algebraic_expr(n_base = n_base,
                                                               decimal_digits = decimal_digits)

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        A number on base {result['n_base']} is written as {result['num']}.
        Express it as an algebraic expression.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        {result['num']} on base {result['n_base']}
        
        = $\\displaystyle 
        {result['num_latex']}
        $ 
        
        = $\\displaystyle {result['pos_values_latex_str']}$
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {result['pos_values_latex_str']}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    
    def _problem_derive_algebraic_expr_two_digit(self, *args, **kwargs):
        """
        _problem_derive_algebraic_expr

        Minimum Grade: 3
        Maximum Grade: 12

        doc_style: xv_doc
        """
        pvhelper = self.base_helper.get_arithmetic_helper().get_positional_value_helper()
        
        n_digits = 2
        x = Symbol('x')
        dx = random.randint(1, 3)

        y_data = []
        y_data.append([
            x + dx,
            f"""
            In {n_digits}-digit number, ones digit is {dx} more than the other digit. 
            """
            ])
        
        y_data.append([
            x - dx,
            f"""
            In {n_digits}-digit number, ones digit is {dx} less than the other digit. 
            """
            ])
        
        y_data.append([
            x * dx,
            f"""
            In {n_digits}-digit number, ones digit is {dx} times the other digit. 
            """
            ])
        
        y, y_expr = random.choice(y_data)

        var_symbs = [x, y]
        
        y_soln_expr = f"""
        Let the tens digit be $\\displaystyle 
        {latex(x)}$. 
        Then, the ones digit is $\\displaystyle 
        {latex(y)}$.
        """
        
        n_base = 10 # random.choice([2, 3, 4, 5, 6, 7, 8, 9, 10, 16])
        
        result = pvhelper.derive_algebraic_expr(n_base = n_base,
                                                n_digits = n_digits,
                                                var_symbs = var_symbs)

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        {y_expr.strip()}
        Express it as an algebraic expression.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        {y_soln_expr.strip()}
        
        The number 
        
        = $\\displaystyle {result['pos_values_latex_str']}$
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {result['pos_values_latex_str']}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    
    def _problem_derive_algebraic_expr_n_digit(self, *args, **kwargs):
        """
        _problem_derive_algebraic_expr

        Minimum Grade: 3
        Maximum Grade: 12

        doc_style: xv_doc
        """
        pvhelper = self.base_helper.get_arithmetic_helper().get_positional_value_helper()
        
        n_digits = 2
        x = Symbol('x')
        dx = random.randint(1, 3)

        y_data = []
        y_data.append([
            x + dx,
            f"""
            In {n_digits}-digit number, ones digit is {dx} more than the other digit. 
            """
            ])
        
        y_data.append([
            x - dx,
            f"""
            In {n_digits}-digit number, ones digit is {dx} less than the other digit. 
            """
            ])
        
        y_data.append([
            x * dx,
            f"""
            In {n_digits}-digit number, ones digit is {dx} times the other digit. 
            """
            ])
        
        y, y_expr = random.choice(y_data)

        var_symbs = [x, y]
        
        y_soln_expr = f"""
        Let the tens digit be $\\displaystyle 
        {latex(x)}$. 
        Then, the ones digit is $\\displaystyle 
        {latex(y)}$.
        """
        
        n_base = 10 # random.choice([2, 3, 4, 5, 6, 7, 8, 9, 10, 16])
        
        result = pvhelper.derive_algebraic_expr(n_base = n_base,
                                                n_digits = n_digits,
                                                var_symbs = var_symbs)

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        {y_expr.strip()}
        Express it as an algebraic expression.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        {y_soln_expr.strip()}
        
        The number 
        
        = $\\displaystyle {result['pos_values_latex_str']}$
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {result['pos_values_latex_str']}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

    
    def _problem_multi_digit_numbers(self, *args, **kwargs):
        """
        Multi digit numbers
        
        doc_style: xv_doc
        """
        from ..modelling import StatementToExprManager
        ke = StatementToExprManager(verbose = self.verbose)
        problem_type = '_problem_multi_digit_numbers'
        
        kwargs['problem_type'] = problem_type
        problemTemplate = ke.getProblemTemplate(*args, **kwargs)
        return problemTemplate

