'''
Created on 16-Apr-2021

@author: rishi_priyansh
'''
from sympy import symbols, Eq, solve, Symbol, latex, sqrt, UnevaluatedExpr
from xv.km.ui import SvgHelper  
import matplotlib.pyplot as plt

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 list_to_str, locatePath
import pathlib


# _wave_mechanics_manager
# WaveMechanicsManager
   
class WaveMechanicsManager(BaseManager):
    '''
    WaveMechanicsManager
    Example:
        ke = WaveMechanicsManager()
        ke.getRandomProblem()
        ke.getRandomProblem(problem_type = 0)
        ...
        
        ke.printProblem()
        ke.printAnswer()
        ke.printSolution()
        
        ke.printProblemTypes()
        
    '''    
    _OPR_TYPE_COUNT = 10
    
    def __new__(cls, *args, **kwargs):
        instance = super(WaveMechanicsManager, cls).__new__(cls, *args, **kwargs)
        for base_class in WaveMechanicsManager.__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_frequency',
            '_problem_period',
            '_problem_derive_eq_for_wave_in_a_stretched_string',
            '_problem_wave_in_a_stretched_string',
            '_problem_sinusoidal_wave',
            '_problem_sinusoidal_wave_wave_speed',
            '_problem_sinusoidal_wave_period',
            '_problem_sinusoidal_wave_wavelength',
            '_problem_sinusoidal_wave_power',
            '_problem_sinusoidal_wave_energy',
            '_problem_wave_speed_lamda_time',
            '_problem_wave_speed_lamda_freq',
            '_problem_electromagnetic_energy',
            '_problem_speed_of_sound_in_an_ideal_gas',
            '_problem_doppler_effect',
            '_problem_electromagnetic_spectrum',
            ])
        pass
    


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

        wm_helper = self.base_helper.get_wave_mechanics_helper()
        
        random_problem_method = random.choice([
            wm_helper.get_relation,
            wm_helper.get_computation,
            wm_helper.get_proportional_computation,
            ])
        result = random_problem_method(
                            'get_frequency',
                            as_strings = True)

        if not result:
            problemTemplate.add_problem("""
            Failed to get data
            """.strip(), 
            "text/html")
            return problemTemplate

        use_cache = kwargs.get('use_cache', True)
        refresh_cache = kwargs.get('refresh_cache', False)

        waves_plotter_2d = self.base_helper.get_waves_plotter_2d()
        plotting_method = waves_plotter_2d.plot_frequency_time_period

        plot_code, svg_str = self.base_helper.call_plot_method(plotting_method,
                                                               use_cache = use_cache,
                                                               refresh_cache = refresh_cache)
        
        
        #wave_plotting_helper = self.base_helper.get_wave_plotting_helper()
        #svg_str, plot_code = wave_plotting_helper.plot_frequency_time_period()
        
        
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        
        soln_s = f"""
        
        <b>See the plots and note the time period and number of seconds required to complete a cycle.</b>
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        problemTemplate.add_solution(svg_str, 'text/svg')

        soln_s = f"""
        <b>Code to plot:</b>
        <pre>{plot_code}</pre>
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        

        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate
    

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

        wm_helper = self.base_helper.get_wave_mechanics_helper()
        
        random_problem_method = random.choice([
            wm_helper.get_relation,
            wm_helper.get_computation,
            wm_helper.get_proportional_computation,
            ])
        result = random_problem_method(
                            'get_period',
                            as_strings = True)

        if not result:
            problemTemplate.add_problem("""
            Failed to get data
            """.strip(), 
            "text/html")
            return problemTemplate
        
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate

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

        wm_helper = self.base_helper.get_wave_helper()
        
        result = wm_helper.eq_for_wave_in_a_stretched_string()

        if not result:
            problemTemplate.add_problem("""
            Failed to get problem
            """.strip(), 
            "text/html")
            return problemTemplate
        
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        

        soln_s = f"""
        <b>Image Test:</b>
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        
        image_path = "resources/images/wave-equation-in-a-string.png"
        rootFolder = pathlib.Path(__file__).parent.absolute()
        image_full_path = locatePath(image_path, 
                               rootFolder = rootFolder,
                               part_match = False,
                               search_type = 'file',
                               )
        if image_full_path:
            problemTemplate.add_solution(image_full_path, 'img/png')
        else:
            soln_s = f"""
            Image not found: {image_path}
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        
        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate
    
    def _problem_wave_in_a_stretched_string(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        wm_helper = self.base_helper.get_wave_mechanics_helper()
        
        random_problem_method = random.choice([
            wm_helper.get_relation,
            wm_helper.get_computation,
            wm_helper.get_proportional_computation,
            ])
        result = random_problem_method(
                            'get_wave_in_a_stretched_string',
                            as_strings = True)

        if not result:
            problemTemplate.add_problem("""
            Failed to get data
            """.strip(), 
            "text/html")
            return problemTemplate
        
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        
        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate
    
    
    def _problem_sinusoidal_wave(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        wm_helper = self.base_helper.get_wave_mechanics_helper()
        
        random_problem_method = random.choice([
            wm_helper.get_relation,
            wm_helper.get_computation,
            wm_helper.get_proportional_computation,
            ])
        result = random_problem_method(
                            'get_sinusoidal_wave',
                            as_strings = True)

        if not result:
            problemTemplate.add_problem("""
            Failed to get data
            """.strip(), 
            "text/html")
            return problemTemplate
        
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate
    
    def _problem_sinusoidal_wave_wave_speed(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        wm_helper = self.base_helper.get_wave_mechanics_helper()
        
        random_problem_method = random.choice([
            wm_helper.get_relation,
            wm_helper.get_computation,
            wm_helper.get_proportional_computation,
            ])
        result = random_problem_method(
                            'get_sinusoidal_wave_wave_speed',
                            as_strings = True)

        if not result:
            problemTemplate.add_problem("""
            Failed to get data
            """.strip(), 
            "text/html")
            return problemTemplate
        
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate
    
    def _problem_sinusoidal_wave_period(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        wm_helper = self.base_helper.get_wave_mechanics_helper()
        
        random_problem_method = random.choice([
            wm_helper.get_relation,
            wm_helper.get_computation,
            wm_helper.get_proportional_computation,
            ])
        result = random_problem_method(
                            'get_sinusoidal_wave_period',
                            as_strings = True)

        if not result:
            problemTemplate.add_problem("""
            Failed to get data
            """.strip(), 
            "text/html")
            return problemTemplate
        
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate
    
    def _problem_sinusoidal_wave_wavelength(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        wm_helper = self.base_helper.get_wave_mechanics_helper()
        
        random_problem_method = random.choice([
            wm_helper.get_relation,
            wm_helper.get_computation,
            wm_helper.get_proportional_computation,
            ])
        result = random_problem_method(
                            'get_sinusoidal_wave_wavelength',
                            as_strings = True)

        if not result:
            problemTemplate.add_problem("""
            Failed to get data
            """.strip(), 
            "text/html")
            return problemTemplate
        
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate
    
    def _problem_sinusoidal_wave_power(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        wm_helper = self.base_helper.get_wave_mechanics_helper()
        
        random_problem_method = random.choice([
            wm_helper.get_relation,
            wm_helper.get_computation,
            wm_helper.get_proportional_computation,
            ])
        result = random_problem_method(
                            'get_sinusoidal_wave_power',
                            as_strings = True)

        if not result:
            problemTemplate.add_problem("""
            Failed to get data
            """.strip(), 
            "text/html")
            return problemTemplate
        
        problem_s = 'todo: to fix dimensions'
        problemTemplate.add_problem(problem_s, "text/html")
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate
    
    def _problem_sinusoidal_wave_energy(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        wm_helper = self.base_helper.get_wave_mechanics_helper()
        
        random_problem_method = random.choice([
            wm_helper.get_relation,
            wm_helper.get_computation,
            wm_helper.get_proportional_computation,
            ])
        result = random_problem_method(
                            'get_sinusoidal_wave_energy',
                            as_strings = True)

        if not result:
            problemTemplate.add_problem("""
            Failed to get data
            """.strip(), 
            "text/html")
            return problemTemplate
        
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate
    
    def _problem_wave_speed_lamda_time(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        wm_helper = self.base_helper.get_wave_mechanics_helper()
        
        random_problem_method = random.choice([
            wm_helper.get_relation,
            wm_helper.get_computation,
            wm_helper.get_proportional_computation,
            ])
        result = random_problem_method(
                            'get_wave_speed_lamda_time',
                            as_strings = True)

        if not result:
            problemTemplate.add_problem("""
            Failed to get data
            """.strip(), 
            "text/html")
            return problemTemplate
        
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate
    
    def _problem_wave_speed_lamda_freq(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        wm_helper = self.base_helper.get_wave_mechanics_helper()
        
        random_problem_method = random.choice([
            wm_helper.get_relation,
            wm_helper.get_computation,
            wm_helper.get_proportional_computation,
            ])
        result = random_problem_method(
                            'get_wave_speed_lamda_freq',
                            as_strings = True)

        if not result:
            problemTemplate.add_problem("""
            Failed to get data
            """.strip(), 
            "text/html")
            return problemTemplate
        
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate
    
    def _problem_electromagnetic_energy(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        wm_helper = self.base_helper.get_wave_mechanics_helper()
        
        random_problem_method = random.choice([
            wm_helper.get_relation,
            wm_helper.get_computation,
            wm_helper.get_proportional_computation,
            ])
        result = random_problem_method(
                            'get_electromagnetic_energy',
                            as_strings = True)

        if not result:
            problemTemplate.add_problem("""
            Failed to get data
            """.strip(), 
            "text/html")
            return problemTemplate
        
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate
    
    def _problem_speed_of_sound_in_an_ideal_gas(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        wm_helper = self.base_helper.get_wave_mechanics_helper()
        
        random_problem_method = random.choice([
            wm_helper.get_relation,
            wm_helper.get_computation,
            wm_helper.get_proportional_computation,
            ])
        result = random_problem_method(
                            'get_speed_of_sound_in_an_ideal_gas',
                            as_strings = True)

        if not result:
            problemTemplate.add_problem("""
            Failed to get data
            """.strip(), 
            "text/html")
            return problemTemplate
        
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate
    
    def _problem_doppler_effect(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        wm_helper = self.base_helper.get_wave_mechanics_helper()
        
        random_problem_method = random.choice([
            wm_helper.get_relation,
            wm_helper.get_computation,
            wm_helper.get_proportional_computation,
            ])
        result = random_problem_method(
                            'get_doppler_effect',
                            as_strings = True)

        if not result:
            problemTemplate.add_problem("""
            Failed to get data
            """.strip(), 
            "text/html")
            return problemTemplate
        
        problemTemplate.add_problem(result['problem_s'].strip(), "text/html")
        problemTemplate.add_solution(result['soln_s'], 'text/html')
        problemTemplate.add_answer(result['answer_s'].strip(), 'text/html')
        return problemTemplate
    
    def _sample(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = fr"""
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = fr"""
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = fr"""
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
    


    
    