'''
Created on 16-Apr-2021

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

import matplotlib.pyplot as plt
from xv.km.ui import SvgHelper  

from sympy import (latex, Symbol, sqrt, simplify)

from sympy.core.expr import UnevaluatedExpr

from xv.km.util import latex_fraction

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


# _vector_2d_manager
# Vector2dManager
        
class Vector2dManager(BaseManager):
    '''
    How to work with quivers
    Example:
        ke = Vector2dManager()
        ke
        
        ke.printProblemTypes()
        
        ke.getRandomProblem()
        ke.getRandomProblem(problem_type = 0)
        ke.getRandomProblem(problem_type = 1)
        ...
        
        ke.printProblem()
        ke.printAnswer()
        ke.printSolution()
        
        ke.printLength(2, 5, 3, -1)
        ke.printMovementLengths()        
        
        
    '''    
    def __new__(cls, *args, **kwargs):
        instance = super(Vector2dManager, cls).__new__(cls, *args, **kwargs)
        for base_class in Vector2dManager.__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_vector_summary',
            '_problem_cartesian_3d_vector',
            '_problem_cartesian_3d_unit_vector',
            '_problem_cartesian_3d_vector_distance',
            '_problem_plot_2d_vector',
            '_problem_plot_2d_vector_and_rotate',
            '_problem_plot_2d_vector_and_move',
            '_problem_plot_2d_vector_and_rotate_and_move',
         ])
        pass
    
    
    
    def _problem_vector_summary(self, *args, **kwargs):
        """

        doc_style: xv_doc
        """
        vh = self.base_helper.get_vector_helper().get_vector_plotter_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        
        problem_s = f"""
        Vector Summary:
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)

        soln_s = r"""

        $\displaystyle \mathbf{a}+(-\mathbf{a})=\mathbf{a}-\mathbf{a}=\mathbf{0}$
        
        $\displaystyle \mathbf{a}+\mathbf{b}+\mathbf{c}=\mathbf{a}+\mathbf{c}+\mathbf{b}=\mathbf{b}+\mathbf{c}+\mathbf{a}$
        
        $\displaystyle (\mathbf{a}+\mathbf{b})+\mathbf{c}=\mathbf{a}+(\mathbf{b}+\mathbf{c})$


        Given that $\lambda, \mu$ are scalars and $\mathbf{a}, \mathbf{b}$ are vectors.

        $\displaystyle 
        \lambda \mathbf{a}
        =\mathbf{a} \lambda 
        \qquad \text { (commutative law) } 
        $
        
        $\displaystyle 
        (\lambda+\mu) \mathbf{a}
        =\lambda \mathbf{a}+\mu \mathbf{a} 
        \qquad \text { (distributive law) } 
        $
        
        $\displaystyle 
        \lambda(\mu \mathbf{a})
        =\mu(\lambda \mathbf{a})
        =(\lambda \mu) \mathbf{a} 
        \qquad \text { (associative law) } 
        $
        
        $\displaystyle 
        \lambda(\mathbf{a}+\mathbf{b})
        =\lambda \mathbf{a}+\lambda \mathbf{b}
        \qquad \text { (distributive law) }
        $

        \begin{aligned}
        & \mathbf{a} \cdot \mathbf{b}=\mathbf{b} \cdot \mathbf{a} \\
        & \\
        & (\lambda \mathbf{a}) \cdot(\mu \mathbf{b})=\lambda \mu \mathbf{a} \cdot \mathbf{b} \\
        & \\
        & \mathbf{a} \cdot(\mathbf{b}+\mathbf{c})=\mathbf{a} \cdot \mathbf{b}+\mathbf{a} \cdot \mathbf{c} \\
        & \\
        & \text { Special cases } \\
        & \\
        & \mathbf{a} \cdot \mathbf{b}=0 \quad \text { if } \mathbf{a}, \mathbf{b} \text { are orthogonal }(\theta=\pi / 2) \\
        & \\
        & \mathbf{a} \cdot \mathbf{b}=|\mathbf{a}||\mathbf{b}|=a b \quad \text { if } \mathbf{a} \text { and } \mathbf{b} \text { are parallel }(\theta=0) \\
        & \\
        & \mathbf{i} \cdot \mathbf{i}=\mathbf{j} \cdot \mathbf{j}=\mathbf{k} \cdot \mathbf{k}=1 \quad \text { and } \quad \mathbf{i} \cdot \mathbf{j}=\mathbf{j} \cdot \mathbf{k}=\mathbf{k} \cdot \mathbf{i}=0
        \end{aligned}



        1. $\mathbf{a} \times \mathbf{b}=|\mathbf{a}||\mathbf{b}| \sin \theta \mathbf{n}=a b \sin \theta \quad \mathbf{n}$,
        where $\mathbf{n}$ is a unit vector normal to the plane containing $\mathbf{a}$ and $\mathbf{b}$ directed in the sense in which a right-handed screw would advance if rotated from a to b (Figure 23.7).
        An alternative and more convenient definition of $\mathbf{a} \times \mathbf{b}$ is
        2. $\quad \mathbf{a} \times \mathbf{b}=\left|\begin{array}{lll}\mathbf{i} & \mathbf{j} & \mathbf{k} \\ a_{1} & a_{2} & a_{3} \\ b_{1} & b_{2} & b_{3}\end{array}\right|$.
        If required, the angle $\theta$ between $\mathbf{a}$ and $\mathbf{b}$ follows from
        3. $\sin \theta=\frac{|\mathbf{a} \times \mathbf{b}|}{a b}$,


        Properties of the vector product. If $\mathbf{a}$ and $\mathbf{b}$ are vectors and $\lambda$ and $\mu$ are scalars, then
        4. $\mathbf{a} \times \mathbf{b}=-\mathbf{a} \times \mathbf{b}$
        (noncommutative)
        5. $(\lambda \mathbf{a}) \times(\mu \mathbf{b})=\lambda \mu \mathbf{a} \times \mathbf{b}$
        (associative property)
        6. $\mathbf{a} \times(\mathbf{b}+\mathbf{c})=\mathbf{a} \times \mathbf{b}+\mathbf{a} \times \mathbf{c}$
        (distributive property)
        Special cases
        7. $\mathbf{a} \times \mathbf{b}=\mathbf{0}$ if $\mathbf{a}$ and $\mathbf{b}$ are parallel $(\theta=0)$
        8. $\mathbf{a} \times \mathbf{b}=a b \mathbf{n} \quad$ if $\mathbf{a}$ and $\mathbf{b}$ are orthogonal $(\theta=\pi / 2)$ (n the unit normal)
        9. $\quad \mathbf{i} \times \mathbf{j}=\mathbf{k}, \quad \mathbf{j} \times \mathbf{k}=\mathbf{i}, \quad \mathbf{k} \times \mathbf{i}=\mathbf{j}$
        10. $\mathbf{i} \times \mathbf{i}=\mathbf{j} \times \mathbf{j}=\mathbf{k} \times \mathbf{k}=\mathbf{0}$


        $\displaystyle \mathbf{a}=a_{1} \mathbf{i}+a_{2} \mathbf{j}+a_{3} \mathbf{k}$ 
        
        $\displaystyle \mathbf{b}=b_{1} \mathbf{i}+b_{2} \mathbf{j}+b_{3} \mathbf{k}$
        $\displaystyle \theta = $ angle between $\mathbf{a}$ and $\mathbf{b}$
        
        The scalar product or dot product or inner product of vectors 
        $$
        \begin{aligned}
        \displaystyle
        & \mathbf{a} \cdot \mathbf{b} \\
        &=|\mathbf{a}||\mathbf{b}| \cos \theta \\
        &=a b \cos \theta \\
        &=a_{1} b_{1}+a_{2} b_{2}+a_{3} b_{3} 
        \end{aligned}
        $$
        


        $\displaystyle \mathbf{a}(\mathbf{b}+\mathbf{c})=\mathbf{a b}+\mathbf{a c}$
        
        $\displaystyle [\mathbf{a b}]=\left|\begin{array}{ccc}\mathbf{i} & \mathbf{j} & \mathbf{k} \\ a_{x} & a_{y} & a_{z} \\ b_{x} & b_{y} & b_{z}\end{array}\right|=\left(a_{y} b_{z}-a_{z} b_{y}\right) \mathbf{i}+\left(a_{z} b_{x}-a_{x} b_{z}\right) \mathbf{j}+\left(a_{x} b_{y}-a_{y} b_{x}\right) \mathbf{k}$
        
        $\displaystyle \frac{d}{d t}(\mathbf{a}+\mathbf{b})=\frac{d \mathbf{a}}{d t}+\frac{d \mathbf{b}}{d t}$
        
        $\displaystyle \frac{d}{d t}(a \mathbf{a})=\frac{d \alpha_{y}}{d t} \mathbf{a}+\alpha \frac{d \mathbf{a}}{d t}$$\quad[\mathbf{a b}]+[\mathbf{a c}]$
        
        $\displaystyle \frac{d}{d t}(\mathbf{a b})=\frac{d \mathbf{a}}{d t} \mathbf{b}+\mathbf{a} \frac{d \mathbf{b}}{d t}$
        
        $\displaystyle \left.\frac{d}{d t}[\mathbf{a b}]=\left[\frac{d \mathbf{a}}{d t} \mathbf{b}\right]+[\mathbf{a}] \frac{d \mathbf{b}}{d t}\right]$


        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {soln_s}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    
    def _problem_cartesian_3d_vector(self, *args, **kwargs):
        """

        doc_style: xv_doc
        """
        vh = self.base_helper.get_vector_helper().get_vector_plotter_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        n1, n2, n3 = np.random.randint(1, 10, 3)
        C, v = vh.get_3d_vector(n1, n2, n3)
        svg_str, self.C_origin = vh.plot_3d_radius_vector(C, v)
        
        problem_s = f"""
        Write the radius vector of a the corner of a cuboid
        whose opposite corner is at origin and its length, width and 
        height are {n1}, {n2} and {n3}.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)

        soln_s = f"""
        $\\displaystyle {latex(v)}$
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')
        
        answer_s = f"""
        $\\displaystyle {latex(v)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        problemTemplate.add_answer(svg_str, 'text/svg')
        
        return problemTemplate
        pass
    
    def _problem_cartesian_3d_vector_distance(self, *args, **kwargs):
        """

        doc_style: xv_doc
        """
        vh = self.base_helper.get_vector_helper().get_vector_plotter_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        p1 = np.random.randint(1, 10, 3)
        p2 = np.random.randint(1, 10, 3)
        
        p1_str = ", ".join([f"{i}" for i in p1])
        p2_str = ", ".join([f"{i}" for i in p2])
        
        C, v1 = vh.get_3d_vector(*p1)
        C, v2 = vh.get_3d_vector(*p2, C = C)
        v = v2 - v1
        
        svg_str = vh.plot_3d_vector(p1, p2)
        
        problem_s = f"""
        Find the distance between {p1_str} and {p2_str}.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)

        soln_s = f"""
        Distance
        
        $\\displaystyle 
        = \\left( {latex(v2)} \\right) - \\left( {latex(v1)} \\right)
        $
        
        $\\displaystyle 
        = {latex(v)}
        $
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')
        
        answer_s = f"""
        $\\displaystyle {latex(v)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        problemTemplate.add_answer(svg_str, 'text/svg')
        
        return problemTemplate
        pass
       
    def _problem_cartesian_3d_unit_vector(self, *args, **kwargs):
        """

        doc_style: xv_doc
        """
        vh = self.base_helper.get_vector_helper().get_vector_plotter_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        n1, n2, n3 = np.random.randint(1, 3, 3)
        
        OP = Symbol('OP')
        vec_OP = Symbol(f"\\vec {{{latex(OP)}}}")
        norm_OP = Symbol(f"\\left \\lVert {latex(vec_OP)} \\right \\lVert")
        OP_len_expr = f"\\sqrt {{{n1}^2 + {n2}^2 + {n3}^2}}"
        
        OP_len = sqrt(n1 ** 2 + n2 ** 2 + n3 ** 2)
        
        C, v = vh.get_3d_vector(n1, n2, n3)
        svg_str, self.C_origin = vh.plot_3d_radius_vector(C, v, 
                                                   unit_vector = True)

        unit_v_expr = latex_fraction(f"{latex(vec_OP)}",
                                     f"{latex(norm_OP)}",
                                     )
        
        unit_v_expr_cal = latex_fraction(latex(v), latex(OP_len))
        
        problem_s = f"""
        Find the length of the vector that is radius vector OP of point
        P({n1}, {n2}, {n3}). 
        Find the unit in the direction of OP.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)

        soln_s = f"""
        $\\displaystyle 
        \\text {{Radius vector}} \\; {latex(vec_OP)}
        = {latex(v)}
        $
    
        The norm or length of a vetor is calculated as follows:
        
        $\\displaystyle 
        \\text {{Length of}} \\; {latex(vec_OP)}
        = {latex(norm_OP)} 
        = {latex(OP)} 
        = {latex(OP_len_expr)}
        = {latex(OP_len)}
        $
        
        
        $\\displaystyle 
        \\text{{Unit vector}} \\;
        \\widehat {{{latex(OP)}}} 
        = {unit_v_expr}
        = {unit_v_expr_cal}
        $
        
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')
        
        answer_s = f"""
        $\\displaystyle {latex(v)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        problemTemplate.add_answer(svg_str, 'text/svg')
        
        return problemTemplate
        pass

    def _problem_plot_2d_vector(self, *args, **kwargs):
        """
        Plot a 2d vector
        
        Minimum Grade: 6
        Maximum Grade: 12
    
        Examples
        --------
        from xv.math.geometry import Vector2dManager
        ke = Vector2dManager()

        ke.getRandomProblem(problem_type = '_problem_plot_2d_vector')

        doc_style: xv_doc
        """

        vh = self.base_helper.get_vector_helper().get_vector_plotter_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        plt.ioff()
        result = vh.plot_2d_vector()
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
        plt.clf()
        plt.close()

        code_str = self.get_source_code(vh.plot_2d_vector)
        
        problem_s = f"""
        Plot a random 2D vector.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')

        soln_s = f"""
        <h2>Sample Code:</h2>
        {code_str}
        
        <h2>Plot:</h2>
        vector = ${latex(result['v'])}$
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')
        
        answer_s = f"""
        vector = ${latex(result['v'])}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        problemTemplate.add_answer(svg_str, 'text/svg')
        
        return problemTemplate
        pass
    
    def _problem_plot_2d_vector_and_rotate(self, *args, **kwargs):
        """
        Plot a 2d vector
        
        Minimum Grade: 6
        Maximum Grade: 12
    
        Examples
        --------
        from xv.math.geometry import Vector2dManager
        ke = Vector2dManager()

        ke.getRandomProblem(problem_type = '_problem_plot_2d_vector_and_rotate')

        doc_style: xv_doc
        """
        vh = self.base_helper.get_vector_helper().get_vector_plotter_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        plt.ioff()
        result = vh.plot_2d_vector_and_rotate()
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
        result['fig'].clear()
        
        code_str = self.get_source_code(vh.plot_2d_vector_and_rotate)
        
        problem_s = f"""
        Plot a random 2D vector and rotate it by a random angle.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')

        soln_s = f"""
        <h2>Sample Code:</h2>
        {code_str}
        
        <h2>Plot:</h2>
        vector = ${latex(result['v'])}$

        angle = ${latex(result['theta'])}^{{\\circ}}$

        rotated vector = ${latex(result['v_rotated'])}$

        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')
        
        answer_s = f"""
        vector = ${latex(result['v'])}$

        angle = ${latex(result['theta'])}^{{\\circ}}$

        rotated vector = ${latex(result['v_rotated'])}$

        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        problemTemplate.add_answer(svg_str, 'text/svg')
        
        return problemTemplate
        pass
    
    def _problem_plot_2d_vector_and_move(self, *args, **kwargs):
        """
        Plot a 2d vector
        
        Minimum Grade: 6
        Maximum Grade: 12
    
        Examples
        --------
        from xv.math.geometry import Vector2dManager
        ke = Vector2dManager()

        ke.getRandomProblem(problem_type = '_problem_plot_2d_vector_and_move')

        doc_style: xv_doc
        """
        vh = self.base_helper.get_vector_helper().get_vector_plotter_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        plt.ioff()
        result = vh.plot_2d_vector_and_move()
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
        plt.clf()
        plt.close()
        
        code_str = self.get_source_code(vh.plot_2d_vector_and_rotate)
        
        problem_s = f"""
        Plot a random 2D vector and move it by random distance.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')

        soln_s = f"""
        <h2>Sample Code:</h2>
        {code_str}
        
        <h2>Plot:</h2>
        vector = ${latex(result['v'])}$

        new origin = ${latex(result['E_origin'])}$

        moved vector = ${latex(result['v_moved'])}$

        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')
        
        answer_s = f"""
        vector = ${latex(result['v'])}$

        new origin = ${latex(result['E_origin'])}$

        moved vector = ${latex(result['v_moved'])}$

        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        problemTemplate.add_answer(svg_str, 'text/svg')
        
        return problemTemplate
        pass
    
    def _problem_plot_2d_vector_and_rotate_and_move(self, *args, **kwargs):
        """
        Plot a 2d vector
        
        Minimum Grade: 6
        Maximum Grade: 12
    
        Examples
        --------
        from xv.math.geometry import Vector2dManager
        ke = Vector2dManager()

        ke.getRandomProblem(problem_type = '_problem_plot_2d_vector_and_rotate_and_move')

        doc_style: xv_doc
        """

        vh = self.base_helper.get_vector_helper().get_vector_plotter_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        plt.ioff()
        result = vh.plot_2d_vector_and_rotate_and_move()
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
        result['fig'].clear()
        
        code_str = self.get_source_code(vh.plot_2d_vector_and_rotate_and_move)
        
        problem_s = f"""
        Plot a random 2D vector and rotate it by a random angle.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')

        soln_s = f"""
        <h2>Sample Code:</h2>
        {code_str}
        
        <h2>Plot:</h2>
        vector = ${latex(result['v'])}$

        angle = ${latex(result['theta'])}^{{\\circ}}$

        rotated vector = ${latex(result['v_rotated'])}$

        new origin = ${latex(result['E_origin'])}$

        moved vector = ${latex(result['v_moved'])}$
        

        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')
        
        answer_s = f"""
        vector = ${latex(result['v'])}$

        angle = ${latex(result['theta'])}^{{\\circ}}$

        rotated vector = ${latex(result['v_rotated'])}$

        new origin = ${latex(result['E_origin'])}$

        moved vector = ${latex(result['v_moved'])}$
        
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        problemTemplate.add_answer(svg_str, 'text/svg')
        
        return problemTemplate
        pass
    
