'''
Created on 01-Apr-2021
@author: rishi_priyansh
'''
import os 

import random
import inspect
import traceback
from sympy import (simplify, sympify, log, symbols, Eq, Matrix, latex, 
                   solve, Rational, diag)

from xv.math.formulae import FormulaSet


from ..helper import BaseHelper

from xv.km.managers import BaseManager
from xv.km.kelements import KProblemTemplate
        
class FormulaManager(BaseManager):
    '''
    Formulae related to Physics


    Minimum Grade: 6
    Maximum Grade: 12


    Examples
    --------
    ke = FormulaManager()
    ke 
    
    ke.printProblemTypes()
    ke.helpProblemType(0)
    
    ke.getRandomProblem()
    ke.getRandomProblem(problem_type = 0)


    ke.printProblem()
    ke.printAnswer()
    ke.printSolution()


    doc_style: xv_doc
    ''' 
    
    def __new__(cls, *args, **kwargs):
        instance = super(FormulaManager, cls).__new__(cls, *args, **kwargs)
        for base_class in FormulaManager.__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_selected_formulae',
            '_problem_get_formulae_for_category',
            '_problem_random_formulae',
            '_problem_random_matching',
            ])
        pass

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

        doc_style: xv_doc
        """

        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        problem_s = f"""
        Summary
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = r"""
        
        $\displaystyle 
        E=\frac{1}{4 \pi \varepsilon_{0}} \frac{q}{r^{2}} 
        $
        
        $\displaystyle 
        E=\frac{\sigma}{\varepsilon_{0} \varepsilon}
        $
        
        $\displaystyle 
        \mathbf{E}=-\nabla \varphi = \varphi_{1}-\varphi_{2}=\int_{1}^{2} E_{l} d l
        $
        
        $\displaystyle \mathbf{P}=x \varepsilon_{0} \mathbf{E}$
        
        $\displaystyle \sigma^{\prime}=P_{n}=x \varepsilon_{0} E_{n}$
        
        $\displaystyle \mathbf{D}=\varepsilon_{0} \mathbf{E}+\mathbf{P}$
        
        $\displaystyle \varepsilon=1+\varkappa$
        
        $\displaystyle \mathbf{D}=\varepsilon_{0} \varepsilon \mathbf{E}$
        
        $\displaystyle \oint D_{n} d S=q$
        
        
        $\displaystyle C=q / V$
        
        $\displaystyle C=\frac{\varepsilon_{0} \varepsilon S}{d}$
        
        $\displaystyle W=1 / 2 \sum q_{i} \varphi_{i}$
        
        $\displaystyle W=C V^{2} / 2$
        
        $\displaystyle w=\frac{E D}{2}$
        
        $\displaystyle \mathbf{j}=\sigma \mathbf{E}$
        
        $\displaystyle w=\sigma E^{2}$
        
        
        $\displaystyle p_{m}=I S$ 
        
        $\displaystyle \mathbf{N}=\left[\mathbf{p}_{m} \mathbf{B}\right]$ 
        
        $\displaystyle W=-\mathbf{p}_{m} \mathbf{B}$
        
         
        $\displaystyle 
        \text{Blot and Savart's law:} \;
        d \mathbf{B}
        =\frac{\mu_{0}}{4 \pi} \frac{I[d \mathbf{l}, \mathbf{r}]}{r^{3}}
        $
        
        
        $\displaystyle 
        \text{Induction of the field produced by direct current:} \;
        B
        =\frac{\mu_{0}}{4 \pi} \frac{2 I}{r}
        $
        
        $\displaystyle 
        \text{Induction of the field produced in the centre of a loop:} \;
        B
        =\frac{\mu_{0}}{4 \pi} \frac{2 \pi I}{r}$
        
        $\displaystyle 
        \text{Induction of the field produced in a solenoid:} \;
        B
        =\mu_{0} n I$
        
        $\displaystyle 
        \text{Definition of the vector H:} \;
        \mathbf{H}
        =\mathbf{B} / \mu_{0}-\mathbf{J}$
        
        $\displaystyle 
        \text{Circulation of the vector H in a constant field:} \;
        \oint H_{l} d l
        =I
        $
        
        $\displaystyle 
        \text{Relation between $\mathbf{J}$ and $\mathbf{H}$ :} \;
        \mathbf{J} = \chi \mathbf{H}$
        
        
        $\displaystyle 
        \text{Relation between $\mu$ and $\chi$:} \;
        \mu=1+\chi$
        
        $\displaystyle 
        \text{Relation between $\mathbf{B}$ and $\mathbf{H}$ } \;
        \mathbf{B}=\mu_{0} \mu \mathbf{H}$
        
        $\displaystyle 
        \text{Lorentz force:} \;
        \mathbf{F}=q[\mathbf{v B}]$
        
        $\displaystyle 
        \text{Ampere's law:} \;
        d \mathbf{F}=I[d \mathbf{l}, \mathbf{B}]$
        
        $\displaystyle 
        \text{Force of interaction of parallel currents:} \;
        F=\frac{\mu_{0}}{4 \pi} \frac{2 I_{1} I_{2}}{d}$
        
        $\displaystyle 
        \text{Emf of induction:} \;
        \mathscr{E}_{i}=-\frac{d \Phi}{d t}$
        
        $\displaystyle 
        \text{Inductance:} \;
        L=\Phi / I$
        
        
        
        $\displaystyle 
        \text{Inductance of a solenoid:} \;
        L =\mu_{0} \mu n^{2} V 
        $
        
        $\displaystyle 
        \text{Energy of the magnetic field produced by current:} \;
        W =\frac{L I^{2}}{2}
        $
        
        $\displaystyle 
        \text{Energy density of magnetic field:} \;
        w =\frac{B H}{2}
        $
        
        Maxwell's equations in integral form
        
        $\displaystyle
        \oint D_{n} d S=\int \rho d V
        $
        $\displaystyle
        \oint E_{l} d b=-\int \dot{B}_{n} d S
        $
        $\displaystyle
        \oint B_{n} d S=0
        $
        $\displaystyle
        \oint H_{l} d l=\int\left(j_{n}+\dot{D}_{n}\right) d S
        $
        
        Maxwell's equations in differential form:
        
        $\displaystyle
        \nabla \cdot \mathbf{D}=\rho
        $
        $\displaystyle
        \nabla \times \mathbf{E}=-\dot{\mathbf{B}}
        $
        $\displaystyle
        \nabla \cdot \mathbf{B}=0
        $
        $\displaystyle
        \nabla \times \mathbf{H}=\mathbf{j}+\dot{\mathbf{D}}
        $
        
        Velocity of an electromagnetic wave in a medium
        $\displaystyle 
        v=1 / \sqrt{\varepsilon_{0} \mu_{0} \varepsilon \mu}$
        
        
        Relation between E and H in an electromagnetic wave
        $\displaystyle 
        E \sqrt{\varepsilon_{0} \varepsilon}=H \sqrt{\mu_{0} \mu}$
        
        
        Poynting vector
        $\displaystyle 
        S=[\mathrm{EH}]$
        
        
        
        $\displaystyle 
        \text{:} \;
        $


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


    def _problem_get_formulae_for_category(self, *args, **kwargs):
        """
        Formula for a given category


        Minimum Grade: 6
        Maximum Grade: 12

    
        Examples
        --------
        from xv.physics.core import FormulaManager
        ke = FormulaManager()

        To list all Categories:
        ke.fh.get_categories()

        To get random problem
        ke.getRandomProblem(problem_type = '_problem_get_formulae_for_category')


        doc_style: xv_doc
        """
        self.fh = self.base_helper.get_formula_helper()
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        category = kwargs.get('category', None)
        if category is None:
            sOut = f"Note: Pass category as parameter"
            categories = self.fh.get_categories()
            category = random.choice(categories)

        rfs = FormulaSet(count = 999, category = category, formula_helper = self.fh)
        category = rfs.category
        formulae_latex = rfs.get_latex_set(expr_type = 'latex_expr', 
                                           include_index = False,
                                           include_description = True,
                                           include_links = True)

        formulae_latex_str = "<ol><li>" + "<br/><br/></li><li>".join(formulae_latex) + "</li></ol>"
        
        problem_s = f"""
        Some important formulae in category: <b>{category}</b>

        <b>Note: </b>to get specific category:
        ke.getRandomProblem(problem_type = 0, category = '{category}')

        <b>Note: </b>to list all Categories:
        ke.fh.get_categories()
        
        """
        problemTemplate.add_problem(problem_s, "text/html")
        
        soln_s = f"""
            {formulae_latex_str}
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


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


    def _problem_random_formulae(self, *args, **kwargs):
        """
        A random formulas


        Minimum Grade: 6
        Maximum Grade: 12

    
        Examples
        --------
        from xv.physics.core import FormulaManager
        ke = FormulaManager()

        To get random problem
        ke.getRandomProblem(problem_type = '_problem_random_formulae')


        doc_style: xv_doc
        """
        self.fh = self.base_helper.get_formula_helper()
        
        
        #implies = symbols(u'\U000021D2')
        rfs = FormulaSet(count = 4, formula_helper = self.fh)
        category = rfs.category
        formulae_found = rfs.formulae
        formulae_selected = random.choice(formulae_found)
        
        
        expr_type = random.choice(['lhs', 'rhs'])
        if expr_type == 'lhs':
            problem_entity = formulae_selected.get_latex_expr(
                expr_type = expr_type)
            
            problem_choices = rfs.get_latex_set(expr_type = 'rhs', include_index = True)
        else:
            problem_entity = formulae_selected.get_latex_expr(expr_type = expr_type)
            problem_choices = rfs.get_latex_set(expr_type = 'lhs', include_index = True)
        
        problem_choices_str = "\n\n".join(problem_choices)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        statement = f"""
        <b>{category}:</b>
        
        <b>${problem_entity}$</b>, {formulae_selected.description},  is equal to:
        
        {problem_choices_str}
        """
        problemTemplate.add_problem(statement, "text/html")
        
        soln_s = f"""
            {formulae_selected.description}
            
            ${formulae_selected.latex_expr}$
        
        <b>Note: For all Categories:</b>
        ke.fh.get_categories()
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


        answer_s = f"""
            {formulae_selected.description}
            
            ${formulae_selected.latex_expr}$
        
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_random_matching(self, *args, **kwargs):
        """
        Match two columns of formulae


        Minimum Grade: 6
        Maximum Grade: 12

    
        Examples
        --------
        from xv.physics.core import FormulaManager
        ke = FormulaManager()

        To get random problem
        ke.getRandomProblem(problem_type = '_problem_random_matching')


        doc_style: xv_doc
        """
        self.fh = self.base_helper.get_formula_helper()
        
        
        #implies = symbols(u'\U000021D2')
        rfs = FormulaSet(count = 4, formula_helper = self.fh)
        category = rfs.category
        formulae_latex = rfs.get_latex_set(expr_type = 'latex_expr', 
                                           include_index = True,
                                           include_description = True)
        
        problem_choices_lhs = rfs.get_latex_set(expr_type = 'lhs', 
                                                include_index = True,
                                                include_description = True,
                                                shuffle = True
                                                )
        
        problem_choices_rhs = rfs.get_latex_set(expr_type = 'rhs', 
                                                include_index = True,
                                                shuffle = True
                                                )
        
        
        problem_choices_lhs_str = "\n\n".join(problem_choices_lhs)
        problem_choices_rhs_str = "\n\n".join(problem_choices_rhs)
        formulae_latex_str = "\n\n".join(formulae_latex)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
                
        statement = f"""
        <b>{category}:</b>
        Match the followings:
        
        <div style="float:left; width:45%; color: red;">
        <b>Set A</b>
        
        {problem_choices_lhs_str}
        </div><div style="float:right; width:45%; color: blue;">
        <b>Set B</b>
        
        {problem_choices_rhs_str}
        </div>
        """
        problemTemplate.add_problem(statement, "text/html")
        
        soln_s = f"""
            {formulae_latex_str}
        
        <b>Note: For all Categories:</b>
        ke.fh.get_categories()
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


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

