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

import random
import inspect
import traceback

from sympy import (symbols, Symbol, latex)
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

# _moment_manager
# MomentManager

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


    def _get_coords_str(self, x, prop = None, idx = None, n = None):
        
        if idx:
            coords = [Symbol(f"{x}_{{{idx}{i}}}") for i in range(3)]
        else:
            coords = [Symbol(f"{x}_{i}") for i in range(3)]
            pass

        if prop:
            coords = [Symbol(f"{prop}{latex(xi)}") for xi in coords]

        if n:
            coords = [f"{latex(xi)}^{n}" for xi in coords]
        else:
            coords = [f"{latex(xi)}" for xi in coords]
            pass
        
        coords_str = ",\; ".join(coords)
        coords_str = f"{coords_str} \\cdots"
        return coords, coords_str
        pass

    def _init_problem_types(self, *args, **kwargs):
        self._append_problem_types([
            '_problem_moment_of_a_point',
            '_problem_moment_of_mass_of_a_point',
            '_problem_moment_of_two_points',
            '_problem_moment_of_mass_of_two_points',
            ])
        pass

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

        x = Symbol('x')
        n = Symbol('n')
        M = Symbol(f"M_{n}")
        
        coords, coords_str = self._get_coords_str(x)
        coords_nth, coords_nth_str = self._get_coords_str(x, n = n)

        problem_s = f"""
        Define nth moment of a point.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        If ${coords_str}$ are coordinates of the point, the nth moment is given by

        $\\displaystyle {latex(M)} = {coords_nth_str}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        If ${coords_str}$ are coordinates of the point, the nth moment is given by

        $\\displaystyle {latex(M)} = {coords_nth_str}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        
    def _problem_moment_of_mass_of_a_point(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        x = Symbol('x')
        m = Symbol('m')
        n = Symbol('n')
        M = Symbol(f"M_{n}")
        
        coords, coords_str = self._get_coords_str(x)
        coords_nth, coords_nth_str = self._get_coords_str(x, prop = m, n = n)

        problem_s = f"""
        Define nth moment of a point.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        If ${coords_str}$ are coordinates of the point, the nth moment is given by

        $\\displaystyle {latex(M)} = {coords_nth_str}$
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        If ${coords_str}$ are coordinates of the point, the nth moment is given by

        $\\displaystyle {latex(M)} = {coords_nth_str}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
    
    def _problem_moment_of_two_points(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        x = Symbol('x')
        n = Symbol('n')
        P1 = Symbol(f"P_1")
        P2 = Symbol(f"P_2")
        M1 = Symbol(f"M_{{1{n}}}")
        M2 = Symbol(f"M_{{2{n}}}")
        M = Symbol(f"M_{n}")
        
        coords_1, coords_1_str = self._get_coords_str(x, idx = 1)
        coords_2, coords_2_str = self._get_coords_str(x, idx = 2)
        coords_1_nth, coords_1_nth_str = self._get_coords_str(x, idx = 1, n = n)
        coords_2_nth, coords_2_nth_str = self._get_coords_str(x, idx = 2, n = n)


        coords_nth_str = [f"\\left({coords_1_nth[i]} + {coords_2_nth[i]}\\right)" 
                          for i in range(3)]
        
        coords_nth_str = ",\; ".join(coords_nth_str)
        coords_nth_str = f"{coords_nth_str}\\cdots"
        
        problem_s = f"""
        Define nth moment of a point.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        If The points are defined as, 
        
        $\\displaystyle {latex(P1)} = {coords_1_str}$ 
        $\\displaystyle {latex(P2)} = {coords_2_str}$ 
        
        The nth moment of the points are given by
        
        $\\displaystyle {latex(M1)} = {coords_1_nth_str}$
        
        $\\displaystyle {latex(M2)} = {coords_2_nth_str}$
        
        $\\displaystyle {latex(M)} = {coords_nth_str}$
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        #$$ = \\sum_{{i=0}}^{{i}} {x}_{{i}}^{{{n}}}$$
        
        answer_s = f"""
        $\\displaystyle {latex(M)} = {coords_nth_str}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
    
    def _problem_moment_of_mass_of_two_points(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        x = Symbol('x')
        m1 = Symbol('m_1')
        m2 = Symbol('m_2')
        n = Symbol('n')
        P1 = Symbol(f"P_1")
        P2 = Symbol(f"P_2")
        M1 = Symbol(f"M_{{1{n}}}")
        M2 = Symbol(f"M_{{2{n}}}")
        M = Symbol(f"M_{n}")
        
        coords_1, coords_1_str = self._get_coords_str(x, idx = 1)
        coords_2, coords_2_str = self._get_coords_str(x, idx = 2)
        coords_1_nth, coords_1_nth_str = self._get_coords_str(x, prop = m1, idx = 1, n = n)
        coords_2_nth, coords_2_nth_str = self._get_coords_str(x, prop = m2, idx = 2, n = n)


        coords_nth_str = [f"\\left({coords_1_nth[i]} + {coords_2_nth[i]}\\right)" 
                          for i in range(3)]
        
        coords_nth_str = ",\; ".join(coords_nth_str)
        coords_nth_str = f"{coords_nth_str}\\cdots"
        
        problem_s = f"""
        
        https://tutorial.math.lamar.edu/classes/calcii/centerofmass.aspx
        
        Define nth moment of a point.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        If The points are defined as, 
        
        $\\displaystyle {latex(P1)} = {coords_1_str}$ 
        $\\displaystyle {latex(P2)} = {coords_2_str}$ 
        
        The nth moment of the points are given by
        
        $\\displaystyle {latex(M1)} = {coords_1_nth_str}$
        
        $\\displaystyle {latex(M2)} = {coords_2_nth_str}$
        
        $\\displaystyle {latex(M)} = {coords_nth_str}$
        
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        #$$ = \\sum_{{i=0}}^{{i}} {x}_{{i}}^{{{n}}}$$
        
        answer_s = f"""
        $\\displaystyle {latex(M)} = {coords_nth_str}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
    
