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

import random
import inspect
import traceback

from sympy import (latex, symbols, pi, Lambda, 
                   Matrix, sqrt, atan2,  Determinant,
                   cos, sin, Derivative, Eq, Symbol, Function, Integral, Abs)
from sympy.core.expr import UnevaluatedExpr
import numpy as np


from xv.km.util import latex_fraction
from xv.util import toIntOrFloat
from xv.km.ui import SvgHelper 


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


# _jacobian_manager
# JacobianManager

class JacobianManager(BaseManager):
    '''
    JacobianManager
    Example:
        ke = JacobianManager()
        ke.getRandomProblem()
        ke.getRandomProblem(problem_type = 0)
        ...
        
        ke.printProblem()
        ke.printAnswer()
        ke.printSolution()
        
        ke.printProblemTypes()
        
    '''    
    
    def __new__(cls, *args, **kwargs):
        instance = super(JacobianManager, cls).__new__(cls, *args, **kwargs)
        for base_class in JacobianManager.__bases__:
            base_class.__init__(instance, *args, **kwargs)
        return instance
    
    def __init__(self, *args, **kwargs):
        self.base_helper = BaseHelper(**kwargs)

    def _init_problem_types(self, *args, **kwargs):
        self._append_problem_types([
            '_problem_coordinate_system_bases_matrix',
            '_problem_jacobian_matrix_statements',
            '_problem_jacobian_transformation_custom_functions',
            '_problem_jacobian_integration_custom_functions',
            ])
        pass

    def _problem_coordinate_system_bases_matrix(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        j_helper = self.base_helper.get_calculus_helper().get_jacobian_helper()
        
        coord_sys_type = random.choice(j_helper.get_coord_sys_types())
        bases_matrix = Matrix(j_helper.get_bases(coord_sys_type))

        problem_s = f"""
        What is the bases matrix in {coord_sys_type} coordinate system?
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        $\displaystyle {latex(bases_matrix)}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\displaystyle {latex(bases_matrix)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_jacobian_matrix_statements(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        j_helper = self.base_helper.get_calculus_helper().get_jacobian_helper()
        data = j_helper.get_jacobian_matrix_data()
        result = j_helper.get_jacobian_matrix_statements(data)

        if result is None:
            problem_s = f"""
            Failed to get jacobian_matrix_statements
            """
            problemTemplate.add_problem(problem_s.strip(), 'text/html')
            return problemTemplate


        problem_s = result['problem_s']
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
            
        soln_s = result['soln_s']
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = result['answer_s']
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_jacobian_transformation_custom_functions(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        j_helper = self.base_helper.get_calculus_helper().get_jacobian_helper()

        func_data = j_helper.get_random_func_data()        
        
        data = j_helper.get_transform_data(
                       bases1 = func_data['bases1'],
                       bases2 = func_data['bases2'],
                       mapping_eqs = func_data['mapping_eqs'],
                       func = func_data['func'],
                       )
        
        
        result = j_helper.get_transform_statements(data)
        
        if result is None:
            problem_s = f"""
            Failed to get transform_statements
            """
            problemTemplate.add_problem(problem_s.strip(), 'text/html')
            return problemTemplate

        problem_s = result['problem_s']
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
            
        soln_s = result['soln_s']
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = result['answer_s']
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        
        return problemTemplate

    def _problem_jacobian_integration_custom_functions(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        j_helper = self.base_helper.get_calculus_helper().get_jacobian_helper()

        func_data = j_helper.get_random_func_data()        
        
        data = j_helper.get_integration_transform_data(
                       bases1 = func_data['bases1'],
                       bases2 = func_data['bases2'],
                       mapping_eqs = func_data['mapping_eqs'],
                       func = func_data['func'],
                       limits2 = func_data['limits2']
                       )
        
        result = j_helper.get_integration_transform_statements(data)

        if result is None:
            problem_s = f"""
            Failed to get transform_statements
            """
            problemTemplate.add_problem(problem_s.strip(), 'text/html')
            return problemTemplate

        problem_s = result['problem_s']
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
            
        soln_s = result['soln_s']
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = result['answer_s']
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        
        return problemTemplate

