'''
Created on 16-Apr-2021

@author: rishi_priyansh

Speed, time, distace, acceleration
cost, qty, rate, inflation
force and syatem of two particles - electrostatic, gravity, some force func
    how they move, energy and momentum
    
force and syatem of two particles - continuous, discrete
ellipse vs circle as orbit


'''


import os 
import numpy as np
import random
import inspect
import traceback

from ..helper import BaseHelper

from xv.km.managers import BaseManager
from xv.km.kelements import KProblemTemplate
from xv.util import toIntOrFloat
        
from sympy import (Symbol, symbols, 
                   Derivative, Integral,
                   UnevaluatedExpr,
                   latex,
                   )

from xv.plotter.helper import DiscreteXyGraphHelper, ContinuousXyGraphHelper


# _decoding_xy_graph_manager
# DecodingXyGraphManager

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


    def _init_problem_types(self, *args, **kwargs):
        self._append_problem_types([
            '_problem_discrete_plot_a_graph',
            '_problem_discrete_coords_of_a_point',
            '_problem_discrete_slope_of_a_graph',
            '_problem_discrete_angle_of_slope_of_a_graph',
            '_problem_discrete_zero_slope_of_a_graph',
            '_problem_discrete_area_of_a_graph',
            '_problem_discrete_absolute_area_of_a_graph',
            '_problem_continuous_plot_a_graph',
            '_problem_continuous_slope_of_a_graph',
            '_problem_continuous_area_of_a_graph',
            '_problem_continuous_absolute_area_of_a_graph',
         ])
        pass


    def _problem_discrete_plot_a_graph(self, *args, **kwargs):
        discrete_graph_helper = self.discrete_xy_graph_helper
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        result = discrete_graph_helper.get_xy_graph()
        

        problem_s = f"""
        Plot a graph of {result['x_label']}({result['x']}) vs {result['y_label']}({result['y']}) graph.
        
        {result['x']} = {result['x_values']}
        {result['y']} = {result['y_values']}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')


        soln_s = f"""
        <b>The graph is shown for (x = {result['x_label']}, y = {result['y_label']}).</b>
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(result['svg_str'], 'text/svg')
        
        soln_s = f"""
        <h2>PLot Code:</h2>
        <pre>{result['plot_code']}</pre>
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(result['svg_str'], 'text/svg')

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

    def _problem_discrete_coords_of_a_point(self, *args, **kwargs):
        discrete_graph_helper = self.discrete_xy_graph_helper
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        result = discrete_graph_helper.get_xy_graph()

        max_mins = [
            [f"maximum {result['x']}", f"{max(result['x_values'])}"],
            [f"minimum {result['x']}", f"{min(result['x_values'])}"],
            
            [f"maximum {result['y']}", f"{max(result['y_values'])}"],
            [f"minimum {result['y']}", f"{min(result['y_values'])}"],
            ]
        
        max_mins = random.sample(max_mins, 2)

        problem_s = f"""
        From the following graph, find {max_mins[0][0]} and {max_mins[1][0]}.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        problemTemplate.add_problem(result['svg_str'], 'text/svg')


        soln_s = f"""
        {max_mins[0][0]} = {max_mins[0][1]}
        {max_mins[1][0]} = {max_mins[1][1]}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        answer_s = f"""
        {max_mins[0][0]} = {max_mins[0][1]}
        {max_mins[1][0]} = {max_mins[1][1]}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_discrete_slope_of_a_graph(self, *args, **kwargs):
        discrete_graph_helper = self.discrete_xy_graph_helper
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        result = discrete_graph_helper.get_xy_graph(slope = True)
        
        slope_result = discrete_graph_helper.get_slope_statement(**result)

        problem_s = f"""
        Find the {result['slope_label']} 
        between {slope_result['start_label']} and {slope_result['end_label']} from the following
        {result['x_label']}({result['x']}) vs {result['y_label']}({result['y']}) graph.
        """.replace("\n", "")
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        problemTemplate.add_problem(result['svg_str'], 'text/svg')
        

        problemTemplate.add_solution(slope_result['svg_str'], 'text/svg')
        soln_s = f"""
        {slope_result['slope_stmt']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {slope_result['slope']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_discrete_zero_slope_of_a_graph(self, *args, **kwargs):
        discrete_graph_helper = self.discrete_xy_graph_helper
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        result = discrete_graph_helper.get_xy_graph(slope = True)
            
        slope_result = discrete_graph_helper.get_zero_slope_statement(**result)

        problem_s = f"""
        Find zero {result['slope_label']} in the following
        {result['x_label']}({result['x']}) vs {result['y_label']}({result['y']}) graph.
        """.replace("\n", "")
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        problemTemplate.add_problem(result['svg_str'], 'text/svg')
        

        problemTemplate.add_solution(slope_result['svg_str'], 'text/svg')
        soln_s = f"""
        {slope_result['zero_slope_stmt']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {slope_result['zero_slopes']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate


    def _problem_discrete_angle_of_slope_of_a_graph(self, *args, **kwargs):
        discrete_graph_helper = self.discrete_xy_graph_helper
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        result = discrete_graph_helper.get_xy_graph(slope = True)
            
        angle_result = discrete_graph_helper.get_angle_of_slope_statement(**result)

        problem_s = f"""
        Find angle of {result['slope_label']} 
        between {angle_result['start_label']} and {angle_result['end_label']} from the following
        {result['x_label']}({result['x']}) vs {result['y_label']}({result['y']}) graph.
        """.replace('\n', '')
        
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        problemTemplate.add_problem(result['svg_str'], 'text/svg')

        problemTemplate.add_solution(angle_result['svg_str'], 'text/svg')
        soln_s = f"""
        {angle_result['angle_stmt']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {latex(angle_result['angle'])}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_discrete_area_of_a_graph(self, *args, **kwargs):
        discrete_graph_helper = self.discrete_xy_graph_helper
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        result = discrete_graph_helper.get_xy_graph(area = True)
        
        area_result = discrete_graph_helper.get_area_statement(**result)

        problem_s = f"""
        Find the {result['area_label']} between {area_result['start_label']} and {area_result['end_label']} from the following
        {result['x_label']}({result['x']}) vs {result['y_label']}({result['y']}) graph.
        """.replace("\n", "")
        
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        problemTemplate.add_problem(result['svg_str'], 'text/svg')


        problemTemplate.add_solution(area_result['svg_str'], 'text/svg')
        soln_s = f"""
        {area_result['area_stmt']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {area_result['area']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_discrete_absolute_area_of_a_graph(self, *args, **kwargs):
        discrete_graph_helper = self.discrete_xy_graph_helper
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        result = discrete_graph_helper.get_xy_graph(area = True)
        
        area_result = discrete_graph_helper.get_area_statement(absolute_area = True, **result)

        problem_s = f"""
        Find the {result['area_label']} 
        between {area_result['start_label']} and {area_result['end_label']} 
        from the following
        {result['x_label']}({result['x']}) vs {result['y_label']}({result['y']}) graph
        """.replace("\n", "")
        
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        problemTemplate.add_problem(result['svg_str'], 'text/svg')


        problemTemplate.add_solution(area_result['svg_str'], 'text/svg')
        soln_s = f"""
        {area_result['area_stmt']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {area_result['area']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate


    def _problem_continuous_plot_a_graph(self, *args, **kwargs):
        continuous_graph_helper = self.continuous_xy_graph_helper
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        
        random_data = continuous_graph_helper.get_random_data()
        result = continuous_graph_helper.get_xy_graph(**random_data)
        

        problem_s = f"""
        Plot a graph of 
        {result['x_label']}({result['x']}) vs {result['y_label']}({result['y']}) graph
        <br/>
        where
        <br/>
        $\\displaystyle {latex(result['y'])} = {latex(result['y_function'])}$
        """.replace("\n", "")
        
        problemTemplate.add_problem(problem_s.strip(), 'text/html')


        soln_s = f"""
        <b>The graph is shown for (x = {result['x_label']}, y = {result['y_label']}).</b>
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(result['svg_str'], 'text/svg')
        
        soln_s = f"""
        <h2>PLot Code:</h2>
        <pre>{result['plot_code']}</pre>
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(result['svg_str'], 'text/svg')

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


    def _problem_continuous_slope_of_a_graph(self, *args, **kwargs):
        continuous_graph_helper = self.continuous_xy_graph_helper
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        random_data = continuous_graph_helper.get_random_data(has_slope = True)
        result = continuous_graph_helper.get_xy_graph(**random_data)
        
        
        
        x0 = continuous_graph_helper.get_random_x0(result['x_range'])         
        slope_result = continuous_graph_helper.get_slope_statement(x0 = x0, **result)

        problem_s = f"""
        Find the {result['slope_label']} 
        at {result['x']} = {x0} in the following
        {result['x_label']}({result['x']}) vs {result['y_label']}({result['y']}) graph
        <br/>
        where
        <br/>
        $\\displaystyle {latex(result['y'])} = {latex(result['y_function'])}$
        """.replace("\n", "")
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        problemTemplate.add_problem(result['svg_str'], 'text/svg')
        


        problemTemplate.add_solution(slope_result['tangent_svg_str'], 'text/svg')
        soln_s = f"""
        {slope_result['general_slope_formula_stmt']}
         
        {slope_result['slope_at_x0_stmt']}
         
        {slope_result['tangent_stmt']}
         
         
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        $\\displaystyle {latex(slope_result['slope_at_x0'])}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate


    def _problem_continuous_area_of_a_graph(self, *args, **kwargs):
        continuous_graph_helper = self.continuous_xy_graph_helper
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        random_data = continuous_graph_helper.get_random_data(has_area = True)
        result = continuous_graph_helper.get_xy_graph(**random_data)

        x_values = list(np.linspace(*result['x_range'], 10))
        x_values = [toIntOrFloat(float(item), 2) for item in x_values]
        x0, x1 = sorted(random.sample(x_values, 2))          
        
        area_result = continuous_graph_helper.get_area_statement(x0 = x0, x1 = x1, **result)

        problem_s = f"""
        Find the {result['area_label']} 
        for {result['x']} between {area_result['x0']}  and {area_result['x1']} 
         from the following
        {result['x_label']}({result['x']}) vs {result['y_label']}({result['y']}) graph.
        <br/>
        where
        <br/>
        $\\displaystyle {latex(result['y'])} = {latex(result['y_function'])}$
        """.replace("\n", "")
        
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        problemTemplate.add_problem(result['svg_str'], 'text/svg')


        problemTemplate.add_solution(area_result['svg_str'], 'text/svg')
        soln_s = f"""
        {area_result['area_stmt']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {area_result['area']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate

    def _problem_continuous_absolute_area_of_a_graph(self, *args, **kwargs):
        continuous_graph_helper = self.continuous_xy_graph_helper
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        random_data = continuous_graph_helper.get_random_data(has_area = True)
        result = continuous_graph_helper.get_xy_graph(**random_data)

        x_values = list(np.linspace(*result['x_range'], 10))
        x_values = [toIntOrFloat(float(item), 2) for item in x_values]
        x0, x1 = sorted(random.sample(x_values, 2))          
        
        area_result = continuous_graph_helper.get_area_statement(x0 = x0, x1 = x1, 
                                                      absolute_area = True, **result)

        problem_s = f"""
        Find the absloute {result['area_label']} 
        for {result['x']} between {area_result['x0']}  and {area_result['x1']} 
         from the following
        {result['x_label']}({result['x']}) vs {result['y_label']}({result['y']}) graph.
        <br/>
        where
        <br/>
        $\\displaystyle {latex(result['y'])} = {latex(result['y_function'])}$
        """.replace("\n", "")
        
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        problemTemplate.add_problem(result['svg_str'], 'text/svg')


        problemTemplate.add_solution(area_result['svg_str'], 'text/svg')
        soln_s = f"""
        {area_result['area_stmt']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {area_result['area']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate


