'''
Created on 01-Apr-2021
@author: bthakur


'''
import random
import inspect
import traceback


from sympy.core.expr import UnevaluatedExpr

from sympy import ( Symbol, latex, solve, simplify, factorial,
                    pi, Rational,
                    exp, sin, cos, tan,
                    asin, acos, atan,
                    sinh, cosh, tanh,
                    log, exp, sqrt,
                    Eq)

from numpy import linspace
import numpy as np

from xv.util import number_to_nth, list_to_str
from xv.util import run_with_timeout

from ..helper import BaseHelper

from xv.km.util import latex_fraction, latex_exponent, wrap_bracket


from xv.km.managers import BaseManager
from xv.km.kelements import KProblemTemplate

# _transform_manager
# TransformManager
        
class TransformManager(BaseManager):
    '''
    TransformManager
    Example:
        ke = TransformManager()
        ke.getRandomProblem()
        ke.getRandomProblem(problem_type = 0)
        ...
        
        ke.printProblem()
        ke.printAnswer()
        ke.printSolution()
        
        ke.printProblemTypes()
        
    '''    
    _OPR_TYPE_COUNT = 10
    
    def __new__(cls, *args, **kwargs):
        instance = super(TransformManager, cls).__new__(cls, *args, **kwargs)
        for base_class in TransformManager.__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_binomial_series_applied',
            '_problem_1_minus_xn',
            '_problem_limit_of_sqrt_log',
            '_problem_power_series_expansion',
            ])
        pass

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

        Minimum Grade: 6
        Maximum Grade: 12

        doc_style: xv_doc
        """
        
        
        binomial_helper = self.base_helper.get_algebra_helper().get_binomial_helper()
        transform_helper = self.base_helper.get_general_helper().get_transform_helper()
        
        x, y, n = transform_helper.get_binomial_expr_set()
        
        bs_expr = binomial_helper.get_expr(x = x, y = y, n = n)
        bs_sum = binomial_helper.get_series_sum(x = x, y = y, n = n)
                
        
        bs_eval_0 = binomial_helper.get_series_latex(x = x, y = y, n = n, 
                                                     evaluate_level = 0)

        bs_eval_1 = binomial_helper.get_series_latex(x = x, y = y, n = n, 
                                                     evaluate_level = 1)
        
        bs_eval_2 = binomial_helper.get_series_latex(x = x, y = y, n = n, 
                                                     evaluate_level = 2)
        
        bs_eval_3 = binomial_helper.get_series_latex(x = x, y = y, n = n, 
                                                     evaluate_level = 3)
        
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        
        problem_s = f"""
        Expand: 
        
        $\\displaystyle {latex(bs_expr)}$
        """
        problemTemplate.add_problem(problem_s, 'text/html')

        soln_s = f"""
        $\\displaystyle {latex(bs_expr)}$
        
        $\\displaystyle = {latex(bs_sum)}$
        
        $\\displaystyle = {bs_eval_0}$
        
        $\\displaystyle = {bs_eval_1}$

        $\\displaystyle = {bs_eval_2}$
        
        $\\displaystyle = {bs_eval_3}$
        
        """

        #$\\displaystyle = {latex(sum(bs))}$
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        answer_s = f"""
        """

        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        
        return problemTemplate
        pass



    def _problem_1_minus_xn(self, *args, **kwargs):
    
        transform_helper = self.base_helper.get_general_helper().get_transform_helper()
        
        expr_dict = transform_helper.get_1_minus_xn_expr_dict()

                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        statement = f"""
        Factorize:
        $\\displaystyle {latex(expr_dict['expr'])}$
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        soln_s = f"""
        $\\displaystyle {latex(expr_dict['expr'])}$

        $\\displaystyle = {latex(expr_dict['factors_0'])}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


        answer_s = f"""
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate


    def _problem_limit_of_sqrt_log(self, *args, **kwargs):
                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        statement = f"""
        Find $\\displaystyle  
        \\lim _{{x \\rightarrow+\\infty}} \\sqrt{{\\ln (x+1)}}-\\sqrt{{\\ln x}}$. 
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        soln_s = f"""
        The limits of both terms of this sum are infinity and using the algebra of limits the form $\\infty-\\infty$ is obtained which is indefinite. By the following sequence of transformations we obtain a quotient whose form is $0 / \\infty$ after replacing the numerator and the denominator by their limits. Thus the limit is 0 .
        $\\displaystyle
        \\begin{{aligned}}
        \\lim _{{x \\rightarrow+\\infty}} \\sqrt{{\\ln (x+1)}}-\\sqrt{{\\ln x}}= & \\lim _{{x \\rightarrow+\\infty}} \\frac{{\\ln (x+1)-\\ln x}}{{\\sqrt{{\\ln (x+1)}}+\\sqrt{{\\ln x}}}} \\\\
        & =\\lim _{{x \\rightarrow+\\infty}} \\frac{{\\ln (1+1 / x)}}{{\\sqrt{{\\ln (x+1)}}+\\sqrt{{\\ln x}}}}=0 .
        \\end{{aligned}}
        $
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


        answer_s = f""""""
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_power_series_expansion(self, *args, **kwargs):
                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        statement = f"""
        Find $\\displaystyle  
        \\lim _{{x \\rightarrow 0}} \\frac{{(1+x)^s-1}}{{x}}
        $ and $\\displaystyle  
        \\lim _{{x \\rightarrow 1}}(\\sqrt[n]{{x}}-1) /(\\sqrt[m]{{x}}-1)
        $ 
        """
        problemTemplate.add_problem(statement.strip(), "text/html")
        
        soln_s = f"""
        
        
         
        $\\displaystyle  
        \\lim _{{x \\rightarrow 0}} \\frac{{(1+x)^s-1}}{{x}}
        $ 
        the algebra of limits leads to the indefinite form $0 / 0$, but if we expand the numerator in a power series and divide it through by $x$, then the result becomes obvious.
        $\\displaystyle 
        \\lim _{{x \\rightarrow 0}} \\frac{{(1+x)^s-1}}{{x}}=\\lim _{{x \\rightarrow 0}} \\frac{{1+s x+O\\left(x^2\\right)-1}}{{x}}=\\lim _{{x \\rightarrow 0}} s+O(x)=s .
        $
        
        
        
        $\\displaystyle  
        \\lim _{{x \\rightarrow 1}}(\\sqrt[n]{{x}}-1) /(\\sqrt[m]{{x}}-1)
        $. 
        Direct substitution leads to the indefinite form $0 / 0$, but power series expansion succeeds.
        $\\displaystyle 
        \\lim _{{x \\rightarrow 1}} \\frac{{\\sqrt[n]{{x}}-1}}{{\\sqrt[m]{{x}}-1}}=\\lim _{{\\varepsilon \\rightarrow 0}} \\frac{{\\sqrt[n]{{1+\\varepsilon}}-1}}{{\\sqrt[m]{{1+\\varepsilon}}-1}}=\\lim _{{\\varepsilon \\rightarrow 0}} \\frac{{\\frac{{1}}{{n}} \\varepsilon+O\\left(\\varepsilon^2\\right)}}{{\\frac{{m}}{{m}} \\varepsilon+O\\left(\\varepsilon^2\\right)}}=\\lim _{{\\varepsilon \\rightarrow 0}} \\frac{{\\frac{{1}}{{n}}+O(\\varepsilon)}}{{\\frac{{1}}{{m}}+O(\\varepsilon)}}=\\frac{{m}}{{n}}
        $
        
        

        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


        answer_s = f""""""
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate


    def _problem_TEMPLATE(self, *args, **kwargs):
                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        statement = f"""
        """
        problemTemplate.add_problem(statement.strip(), "text/html",
                                     add_html_line_break = False)
        
        soln_s = f"""
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html',
                                     add_html_line_break = False)


        answer_s = f""""""
        problemTemplate.add_answer(answer_s.strip(), 'text/html',
                                     add_html_line_break = False)
        
        return problemTemplate

