'''
Created on 16-Apr-2021

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

from xv.km.ui import SvgHelper         


from sympy import ( symbols, diff, Symbol, Mul, Matrix, 
                    factorial, sympify, Rational,
                    Derivative, factor,
                    exp, sin, cos, cosh, sinh, pi, sqrt, atan, latex)

from sympy.core.numbers import One, Integer
import sympy as sp


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

# _circle_manager
# CircleManager
        
class CircleManager(BaseManager):
    '''
    Circle and its geometry
    Example:
        ke = CircleManager()
        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(CircleManager, cls).__new__(cls, *args, **kwargs)
        for base_class in CircleManager.__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_plot_unit_circle_center_origin',
            '_problem_plot_concentric_circles_center_origin',
            '_problem_plot_cylinder',
            '_problem_plot_cone',
            '_problem_plot_hemisphere',
            '_problem_plot_sphere',
            '_problem_plot_unit_circle',
            '_problem_plot_unit_circle_divided_into_n_sides',
            '_problem_plot_unit_circle_mark_angles',
            '_problem_plot_unit_circle_mark_points',
            '_problem_plot_unit_circle_with_radius_at_theta',
            '_problem_plot_unit_circle_mark_sin_cos',
            '_problem_plot_unit_circle_argand_diagram',
            '_problem_plot_unit_circle_mark_sin_cos_coordinates',
            '_problem_plot_sin_cos_func_of_pi_theta',
            '_problem_plot_unit_circle_relation_sin_cos_pi_by_2',
         ])
        pass


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

        doc_style: xv_doc
        """

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_unit_circle_center_origin
        
        result = circle_helper.get_plot_code_and_svg_str(plot_method)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Error: Failed to plot the unit circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        
        prob_s = f"""
        Draw a unit circle.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{result['plot_code']}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

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

        doc_style: xv_doc
        """

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_concentric_circles_center_origin
        
        result = circle_helper.get_plot_code_and_svg_str(plot_method)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Error: Failed to plot concentric circles.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        
        prob_s = f"""
        Plot a few concentric circles.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{result['plot_code']}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

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

        doc_style: xv_doc
        """

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_cylinder
        
        result = circle_helper.get_plot_code_and_svg_str(plot_method)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Error: Failed to plot cylinder using circles.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        
        prob_s = f"""
        Draw a cylinder using circles.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{result['plot_code']}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass


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

        doc_style: xv_doc
        """

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_cone
        
        result = circle_helper.get_plot_code_and_svg_str(plot_method)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Error: Failed to plot cone using circles.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        
        prob_s = f"""
        Draw a cone using circles.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{result['plot_code']}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass



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

        doc_style: xv_doc
        """

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_hemisphere
        
        result = circle_helper.get_plot_code_and_svg_str(plot_method)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Error: Failed to plot hemisphere using circles.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        
        prob_s = f"""
        Draw a hemisphere using circles.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{result['plot_code']}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    


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

        doc_style: xv_doc
        """

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_sphere
        
        result = circle_helper.get_plot_code_and_svg_str(plot_method)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Error: Failed to plot sphere using circles.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        
        prob_s = f"""
        Draw a sphere using circles.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{result['plot_code']}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    
    

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

        doc_style: xv_doc
        """

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_unit_circle
        
        result = circle_helper.get_plot_code_and_svg_str(plot_method)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Error: Failed to plot a unit circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        
        prob_s = f"""
        Draw a unit circle.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{result['plot_code']}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    
    
    def _problem_plot_unit_circle_divided_into_n_sides(self, *args, **kwargs):
        """

        doc_style: xv_doc
        """

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_unit_circle_divided_into_n_sides
        
        point_count = random.randint(2, 10)
        svg_str_prob  = plot_method(point_count = point_count, show_point_lables = False)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if svg_str_prob is None:
            prob_s = f"""
            Error: Failed to plot a unit circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        result = circle_helper.get_plot_code_and_svg_str(plot_method,
                                                         point_count = point_count)

        if result is None:
            prob_s = f"""
            Error: Failed to plot a unit circle with angle of the points on the circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            

        # set show_point_lables = True in plot_code      
        plot_code = result['plot_code']
        repl_text = """kwargs.get('show_point_lables', True)"""
        plot_code = plot_code.replace(repl_text, 'True')
        
        repl_text = """point_count = kwargs.get('point_count')"""
        new_text = f"""point_count = {point_count}"""
        plot_code = plot_code.replace(repl_text, new_text)
        
        prob_s = f"""
        Divide the circle into {point_count} parts and mark the points.
        {svg_str_prob}
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{plot_code}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass

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

        doc_style: xv_doc
        """

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_unit_circle_mark_angles
        
        svg_str_prob  = plot_method(show_point_lables = False)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if svg_str_prob is None:
            prob_s = f"""
            Error: Failed to plot a unit circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        result = circle_helper.get_plot_code_and_svg_str(plot_method)

        if result is None:
            prob_s = f"""
            Error: Failed to plot a unit circle with angle of the points on the circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            

        # set show_point_lables = True in plot_code      
        plot_code = result['plot_code']
        repl_text = """kwargs.get('show_point_lables', True)"""
        plot_code = plot_code.replace(repl_text, 'True')
        
        prob_s = f"""
        Mark the angle of the points on the circle.
        {svg_str_prob}
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{plot_code}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    
    

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

        doc_style: xv_doc
        """

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_unit_circle_mark_points
        
        svg_str_prob  = plot_method(show_point_lables = False)

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if svg_str_prob is None:
            prob_s = f"""
            Error: Failed to plot a unit circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        result = circle_helper.get_plot_code_and_svg_str(plot_method)

        if result is None:
            prob_s = f"""
            Error: Failed to plot a unit circle with coordinates of the points on the circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            


        # set show_point_lables = True in plot_code      
        plot_code = result['plot_code']
        repl_text = """kwargs.get('show_point_lables', True)"""
        plot_code = plot_code.replace(repl_text, 'True')
        
        
        prob_s = f"""
        Mark the coordinates of the points on the circle.
        {svg_str_prob}
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{plot_code}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    
    
    def _problem_plot_unit_circle_with_radius_at_theta(self, *args, **kwargs):
        """

        doc_style: xv_doc
        """

        theta_degrees = random.randint(0, 360)
        theta_degrees_ltx = f"{latex(theta_degrees)}^{{\\circ}}"

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_unit_circle_with_radius_at_theta
        
        
        
        result = circle_helper.get_plot_code_and_svg_str(plot_method, theta_degrees = theta_degrees)

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Error: Failed to plot a unit circle with radius at angle ${theta_degrees_ltx}$.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        
        # set value of theta in plot_code   
        plot_code = result['plot_code']
        repl_text = """kwargs.get('theta_degrees', 0)"""
        new_text = f""" {repr(theta_degrees)} """
        plot_code = plot_code.replace(repl_text, new_text)
        
        
        prob_s = f"""
        Draw a unit circle with radius at angle ${theta_degrees_ltx}$. 
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{plot_code}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    
    
    def _problem_plot_unit_circle_mark_sin_cos(self, *args, **kwargs):
        """

        doc_style: xv_doc
        """

        theta = Symbol('theta')

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_unit_circle_mark_sin_cos
        
        result = circle_helper.get_plot_code_and_svg_str(plot_method)

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Error: Failed to plot a unit circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        
        plot_code = result['plot_code']
        
        prob_s = f"""
        Plot a unit circle and mark ${latex(cos(theta))}$ and ${latex(sin(theta))}$.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{plot_code}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    

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

        doc_style: xv_doc
        """

        theta = Symbol('theta')

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_unit_circle_argand_diagram
        
        result = circle_helper.get_plot_code_and_svg_str(plot_method)

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Error: Failed to plot a unit circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        
        plot_code = result['plot_code']
        
        prob_s = f"""
        Plot a unit circle and mark ${latex(cos(theta))}$ and ${latex(sin(theta))}
        $ in argand space.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{plot_code}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
 


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

        doc_style: xv_doc
        """

        theta = Symbol('theta')

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_unit_circle_mark_sin_cos_coordinates
        
        result = circle_helper.get_plot_code_and_svg_str(plot_method)

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Error: Failed to plot a unit circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        
        plot_code = result['plot_code']
        
        prob_s = f"""
        Plot a unit circle and mark points in terms of ${latex(cos(theta))}
        $ and ${latex(sin(theta))}$.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        <pre>{plot_code}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass


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

        doc_style: xv_doc
        """

        theta_symb = Symbol('theta')

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_sin_cos_func_of_pi_theta
        
        result = circle_helper.get_plot_code_and_svg_str(plot_method)

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Error: Failed to plot a unit circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
        
        
        result_conclusion = circle_helper.get_conclusion_sin_cos_func_of_pi_theta()
            
        
        plot_code = result['plot_code']
        
        prob_s = f"""
        Plot a unit circle and mark points for the following angles:
        1. ${latex(theta_symb)}$
        2. ${latex(-theta_symb)}$
        3. ${latex(sp.pi)} - {latex(theta_symb)}$
        4. ${latex(sp.pi)} + {latex(theta_symb)}$
        
        Draw conclusion about sin and cos relations the above angles.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        {result_conclusion['soln_s'].strip()}
        
        <h3>Code to draw plot:</h3>
        <pre>{plot_code}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass


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

        doc_style: xv_doc
        """

        theta = Symbol('theta')

        circle_helper = self.base_helper.get_geometry_helper().get_circle_helper()
        plot_method = circle_helper.plot_unit_circle_relation_sin_cos_pi_by_2
        
        result = circle_helper.get_plot_code_and_svg_str(plot_method)

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        if result is None:
            prob_s = f"""
            Error: Failed to plot a unit circle.
            """
            problemTemplate.add_problem(prob_s.strip(), 'text/html')
            return problemTemplate
            
        
        plot_code = result['plot_code']
        
        prob_s = f"""
        Plot a unit circle and show relation between cos and sin of $
        {latex(theta)}$ and $\\displaystyle 
        {latex(sp.pi / 2)} - {latex(theta)}$.
        """
        problemTemplate.add_problem(prob_s.strip(), 'text/html')
        
        soln_s = f"""
        As sin is opposite side of the angle in a triangle, cos the adjacent. 
        We can easily see that:
        
        $\\displaystyle cos({latex(theta)}) = sin({latex(sp.pi / 2)} - {latex(theta)})$
        
        $\\displaystyle sin({latex(theta)}) = cos({latex(sp.pi / 2)} - {latex(theta)})$
        <h3>Code to draw plot:</h3>
        <pre>{plot_code}</pre>
        """
        
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['svg_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        pass
    
