'''
Created on 02-Mar-2021

@author: bthakur
'''
'''
Created on 07-Feb-2021

@author: bthakur
'''
import numpy as np
import random
import inspect
import traceback

from sympy import (simplify, sympify, log, latex, Symbol, symbols, 
                   UnevaluatedExpr as ueval)

from xv.km.util import latex_fraction
from xv.km.kelements import KPrintElement
        
from ..helper import BaseHelper
from xv.km.managers import BaseManager
from xv.km.kelements import KProblemTemplate
from xv.km._sympy_extension import CustomLogarithm as Log 
        
class LogarithmManager(BaseManager):
    '''
    Logarithm Concepts
    Example:
        ke = LogarithmManager()
        ke.getRandomProblem()
        ke.getRandomProblem(problem_type = 0)
        ke.getRandomProblem(problem_type = 1)
        ...
        ke.printProblem()
        ke.printAnswer()
        ke.printSolution()

        ke.printProblemTypes()
    '''    
    def __new__(cls, *args, **kwargs):
        instance = super(LogarithmManager, cls).__new__(cls, *args, **kwargs)
        for base_class in LogarithmManager.__bases__:
            base_class.__init__(instance, *args, **kwargs)
        return instance
    
    def __init__(self, *args, **kwargs):
        self.base_helper = BaseHelper(**kwargs)
        pass

    def _init_data(self):
        b1 = random.choice([random.randint(2, 9),
                            Symbol('a')
                            ])
        e1, e2, e3, e4, e5 = random.sample(list(range(2, 10)), 5)

        e1_large = random.randint(20, 100)
        b1_expr = " * ".join([str(b1) for x in range(e1)])
        b2_expr = " * ".join([str(b1) for x in range(e2)])
        b3_expr = " * ".join([str(b1) for x in range(e3)])
        b1_b2_expr = " * ".join([f"({b1_expr})" for x in range(e2)])
        return b1, e1, e2, e3, e4, e5, e1_large, b1_expr, b2_expr, b3_expr, b1_b2_expr
        pass

    def _init_problem_types(self, *args, **kwargs):
        self._append_problem_types([
            '_problem_concept_of_log',
            '_problem_general_concept_of_log',
            '_problem_why_concept_of_log',
            '_problem_simple_log_expr',
            '_problem_find_log_of_product_series',
            '_problem_find_log_of_product_of_pairs',
            '_problem_log_of_division',
            '_problem_find_log_of_exp_to_exp',
            '_problem_find_log_of_exp_product',
            '_problem_find_log_of_div_exp_both',
            '_problem_log_and_exponent',
            '_problem_log_reciprocal',
            '_problem_log_chain_rule',
            '_problem_product_of_two_terms',
            '_problem_div_of_two_terms',
            '_problem_simplify_log_in_exponent',
            '_problem_log_of_multi_terms',
            '_problem_long_expanded_to_simplified',
            '_problem_log_of_common_numbers',
            #'_problem_custom_log_expr',
            '_problem_custom_questions',
         ])
        pass


    def _problem_concept_of_log(self, *args, **kwargs):
        b = 10
        
        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_concept_of_log(b)
        
        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_general_concept_of_log(self, *args, **kwargs):
        b = random.choice([2, 3, 5, 10])
        
        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_concept_of_log(b)
        
        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_why_concept_of_log(self, *args, **kwargs):
        
        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_why_concept_of_log()
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        {result['prob_s']}
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        #{result['svg_html']}

        soln_s = f"""
        {result['soln_s']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        #problemTemplate.add_solution(svg_str, 'text/svg')

        answer_s = f"""
        {result['answer_s']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        problemTemplate.add_answer(result['svg_str'], 'text/svg')
        
        return problemTemplate
        pass


    def _problem_simple_log_expr(self, *args, **kwargs):
        b1, e1, e2, e3, e4, e5, e1_large, b1_expr, b2_expr, b3_expr, b1_b2_expr = self._init_data()

        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_simple_log_expr(b1, b1_expr, e1)
        
        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_find_log_of_product_series(self, *args, **kwargs):
        b1, e1, e2, e3, e4, e5, e1_large, b1_expr, b2_expr, b3_expr, b1_b2_expr = self._init_data()

        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_find_log_of_product_series(b1, e1_large)
        
        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_find_log_of_product_of_pairs(self, *args, **kwargs):
        b1, e1, e2, e3, e4, e5, e1_large, b1_expr, b2_expr, b3_expr, b1_b2_expr = self._init_data()

        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_find_log_of_product_of_pairs(b1, b1_expr, b2_expr, e1, e2)

        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_log_of_division(self, *args, **kwargs):
        b1, e1, e2, e3, e4, e5, e1_large, b1_expr, b2_expr, b3_expr, b1_b2_expr = self._init_data()

        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_log_of_division(b1, b1_expr, b2_expr, e1, e2)

        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_find_log_of_exp_to_exp(self, *args, **kwargs):
        b1, e1, e2, e3, e4, e5, e1_large, b1_expr, b2_expr, b3_expr, b1_b2_expr = self._init_data()

        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_find_log_of_exp_to_exp(b1, b1_expr, e1, e3)

        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_find_log_of_exp_product(self, *args, **kwargs):
        b1, e1, e2, e3, e4, e5, e1_large, b1_expr, b2_expr, b3_expr, b1_b2_expr = self._init_data()

        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_find_log_of_exp_product(b1, b1_expr, b2_expr, e1, e2, e3, e4)

        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_find_log_of_div_exp_both(self, *args, **kwargs):
        (b1, 
         e1, e2, e3, e4, e5, e1_large, 
         b1_expr, b2_expr, b3_expr, b1_b2_expr) = self._init_data()

        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_find_log_of_div_exp_both(b1, b1_expr, b2_expr, e1, e2, e3, e4)

        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_log_and_exponent(self, *args, **kwargs):
        a, b, x = symbols('a, b, x')
        if random.choice([True, False]):
            a, b = random.sample(range(1, 10), 2)

        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_log_reciprocal(a, b, x)

        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_log_reciprocal(self, *args, **kwargs):
        if random.choice([True, False]):
            a, b, c = random.sample(range(2, 10), 3)
        else:
            a, b, c = symbols('a, b, c')
            pass

        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_log_chain_rule(a, b, a)

        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_log_chain_rule(self, *args, **kwargs):
        if random.choice([True, False]):
            x, y, z = random.sample(range(2, 10), 3)
        else:
            x, y, z = symbols('a, b, c')
            pass

        lst = [x, y, z]
        while random.choice([True, False]):
            random.shuffle(lst)
            lst[0] = lst[0] + lst[0]
            lst[1] = lst[0] * lst[0]
            pass
        a, b, c = lst
            
        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_log_chain_rule(a, b, c)

        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_product_of_two_terms(self, *args, **kwargs):
        if random.choice([True, False]):
            b, a1, a2 = random.sample(range(2, 10), 3)
        else:
            b, a1, a2 = symbols('a, b, c')
            pass

            
        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_product_of_two_terms(b, a1, a2)

        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_div_of_two_terms(self, *args, **kwargs):
        if random.choice([True, False]):
            b, a1, a2 = random.sample(range(2, 10), 3)
        else:
            b, a1, a2 = symbols('a, b, c')
            pass

        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_div_of_two_terms(b, a1, a2)

        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_simplify_log_in_exponent(self, *args, **kwargs):
        if random.choice([True, False]):
            b, a1, a2 = random.sample(range(2, 10), 3)
        else:
            b, a1, a2 = symbols('a, b, c')
            pass

        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_simplify_log_in_exponent(b, a1)

        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_log_of_multi_terms(self, *args, **kwargs):
        if random.choice([True, False]):
            b, a1, a2, a3, e1 = random.sample(range(2, 10), 5)
        else:
            b, a1, a2, a3, e1 = symbols('a, b, c, d, n')
            pass

        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_log_of_multi_terms(b, a1, a2, a3, e1)

        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_long_expanded_to_simplified(self, *args, **kwargs):
        list1 = list(symbols('a, b, c, d, n', positive = True))
        list1.extend(random.sample(range(2, 10), 5)) 
        
        b, a1, a2, a3, e1 = random.sample(list1, 5)


        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_long_expanded_to_simplified(b, a1, a2, a3)

        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_custom_log_expr(self, *args, **kwargs):
        
        expr = kwargs.get('expr', None)
        
        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_custom_log_expr(expr)

        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_log_of_common_numbers(self, *args, **kwargs):
        
        log_helper = self.base_helper.get_arithmetic_helper().get_logarithm_helper()
        result = log_helper.problem_log_of_common_numbers()
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        prob_s = f"""
        {result['prob_s']}
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')

        #{result['svg_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_custom_questions(self, *args, **kwargs):
        
        #file:///C:/Users/b%20thakur/Downloads/Logarithm%20And%20Its%20Applications%20(G.%20Tewani)%20(z-lib.org).pdf
        
        
        from ..storeddata import LogarithmStoredQuestions
        keywords = kwargs.get('keywords', 'log')
        
        lsqs = LogarithmStoredQuestions(verbose = self.verbose)
        result = lsqs.get_random_question(keywords)

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Failed to get questions
            keywords = {keywords}
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        
        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

