'''
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.core.expr import UnevaluatedExpr as ueval
from xv.km.util import latex_fraction

from ..helper import BaseHelper
from xv.km.managers import BaseManager
from xv.km.kelements import KProblemTemplate
import matplotlib.pylab as plt
import numpy as np

class FourierSeriesManager(BaseManager):
    '''
    Circle and its geometry
    Example:
        ke = FourierSeriesManager()
        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(FourierSeriesManager, cls).__new__(cls, *args, **kwargs)
        for base_class in FourierSeriesManager.__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_a_wave',
            '_problem_plot_multiple_waves',
            '_problem_plot_a_fourier_wave',
            '_problem_plot_fourier_waves',
            '_problem_plot_a_circular_wave',
            '_problem_plot_multiple_circular_wave',
            '_problem_plot_a_circular_fourier_wave',
            '_problem_plot_circular_fourier_wave',
            '_problem_fourier_expression',
            '_problem_complex_fourier_expression',
            
            
         ])
        pass
    

    def _problem_plot_a_wave(self, *args, **kwargs):
        fourier_helper = self.base_helper.get_fourier_helper()
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        # point_count = kwargs.get('point_count', 1000)
        
        data = kwargs.get('data', [1, 3, 0])
        steps = 1000
        start_theta = kwargs.get('start_theta', 0)
        end_theta = kwargs.get('end_theta', 2*np.pi)
        theta = np.linspace(start_theta, end_theta, steps)
        
        plt.ioff()
        plt.figure(figsize = (13, 6))
        fourier_helper.plot_wave(data, theta)
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
        #plt.figure().clear()
        plt.clf()
        plt.close()

        
        statement = f"""
        Plot a wave.

        ________________________
        You can provide data for different waves [Amplitude, frequancy, phase difference]
        Example
        data = [1, 3, 0]
        ke.getRandomProblem(problem_type = 0, data = data)
        """
        problemTemplate.add_problem(statement.strip(), 'text/html')

        soln_s = f"""
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')

        
        problemTemplate.add_answer(svg_str, 'text/svg')

        return problemTemplate
        pass
    
    def _problem_plot_multiple_waves(self, *args, **kwargs):
        fourier_helper = self.base_helper.get_fourier_helper()
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        # point_count = kwargs.get('point_count', 1000)
        
        data = kwargs.get('data', [
            [1, 3, 0],
            [7, 2, 0],
            [3, 6, np.pi/4],
        ])
        steps = 1000
        start_theta = kwargs.get('start_theta', 0)
        end_theta = kwargs.get('end_theta', 2*np.pi)
        theta = np.linspace(start_theta, end_theta, steps)
        
        plt.ioff()
        fourier_helper.plot_multiple_waves(data, time = theta)
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
        #plt.figure().clear()
        plt.clf()
        plt.close()


        
        statement = f"""
        Plot a wave.

        ________________________
        You can provide data for different waves [Amplitude, frequancy, phase difference]
        Example
        data = [
            [1, 3, 0],
            [7, 2, 0],
            [3, 6, np.pi/4],
        ]
        ke.getRandomProblem(problem_type = 1, data = data)
        """
        problemTemplate.add_problem(statement.strip(), 'text/html')

        soln_s = f"""
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')

        
        problemTemplate.add_answer(svg_str, 'text/svg')

        return problemTemplate
        pass
    
    def _problem_plot_a_fourier_wave(self, *args, **kwargs):
        fourier_helper = self.base_helper.get_fourier_helper()
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        data = kwargs.get('data', [
            [1, 3, 0],
            [7, 2, 0],
            [3, 6, np.pi/4],
        ])
        alpha = kwargs.get('alpha', 1)
        steps = 1000
        start_theta = kwargs.get('start_theta', 0)
        end_theta = kwargs.get('end_theta', 2*np.pi)
        theta = np.linspace(start_theta, end_theta, steps)
        
        plt.ioff()
        plt.figure(figsize = (13, 6))
        fourier_helper.plot_a_fourier_waves(data, time = theta)
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
        #plt.figure().clear()
        plt.clf()
        plt.close()


        
        statement = f"""
        Plot fourier waves.

        ________________________
        You can provide data for different waves [Amplitude, frequancy, phase difference]
        Example
        data = [
            [1, 3, -np.pi/2],
            [4, 7, 0],
            [9, 3, np.pi/4],
            [2, 5, np.pi],
        ]
        ke.getRandomProblem(problem_type = 2, data = data)
        """
        problemTemplate.add_problem(statement.strip(), 'text/html')

         
        soln_s = f"""
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')

        
        problemTemplate.add_answer(svg_str, 'text/svg')

        return problemTemplate
        pass
    
    def _problem_plot_fourier_waves(self, *args, **kwargs):
        fourier_helper = self.base_helper.get_fourier_helper()
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        # point_count = kwargs.get('point_count', 1000)
        
        data = kwargs.get('data', [
            [1, 3, 0],
            [7, 2, 0],
            [3, 6, np.pi/4],
        ])
        alpha = kwargs.get('alpha', 1)
        steps = 1000
        start_theta = kwargs.get('start_theta', 0)
        end_theta = kwargs.get('end_theta', 2*np.pi)
        theta = np.linspace(start_theta, end_theta, steps)
        
        plt.ioff()
        fourier_helper.plot_fourier_waves(data, time = theta, alpha = alpha)
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
        #plt.figure().clear()
        plt.clf()
        plt.close()


        
        statement = f"""
        Plot fourier waves.

        ________________________
        You can provide data for different waves [Amplitude, frequancy, phase difference]
        Example
        data = [
            [1, 3, -np.pi/2],
            [4, 7, 0],
            [9, 3, np.pi/4],
            [2, 5, np.pi],
        ]
        ke.getRandomProblem(problem_type = 1, data = data)
        """
        problemTemplate.add_problem(statement.strip(), 'text/html')

         
        soln_s = f"""
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')

        
        problemTemplate.add_answer(svg_str, 'text/svg')

        return problemTemplate
        pass
    
    def _problem_plot_a_circular_wave(self, *args, **kwargs):
        fourier_helper = self.base_helper.get_fourier_helper()
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        # point_count = kwargs.get('point_count', 1000)
        
        data = kwargs.get('data', [1, 3, 0])
        steps = 1000
        start_theta = kwargs.get('start_theta', 0)
        end_theta = kwargs.get('end_theta', 2*np.pi)
        theta = np.linspace(start_theta, end_theta, steps)
        
        plt.ioff()
        plt.figure(figsize = (13, 6))
        fourier_helper.plot_circular_wave(data, theta)
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
        #plt.figure().clear()
        plt.clf()
        plt.close()


        
        statement = f"""
        Plot a wave.

        ________________________
        You can provide data for different waves [Amplitude, frequancy, phase difference]
        Example
        data = [1, 3, 0]
        ke.getRandomProblem(problem_type = 0, data = data)
        """
        problemTemplate.add_problem(statement.strip(), 'text/html')

        soln_s = f"""
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')

        
        problemTemplate.add_answer(svg_str, 'text/svg')

        return problemTemplate
        pass
    
    def _problem_plot_multiple_circular_wave(self, *args, **kwargs):
        fourier_helper = self.base_helper.get_fourier_helper()
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        data = kwargs.get('data', [
            [1, 3, 0],
            [7, 2, 0],
            [3, 6, np.pi/4],
        ])
        steps = 1000
        start_theta = kwargs.get('start_theta', 0)
        end_theta = kwargs.get('end_theta', 2*np.pi)
        theta = np.linspace(start_theta, end_theta, steps)
        
        plt.ioff()
        fourier_helper.plot_multiple_circular_waves(data, time = theta)
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
        #plt.figure().clear()
        plt.clf()
        plt.close()


        
        statement = f"""
        Plot circular fourier wave.

        ________________________
        You can provide data for different waves [Amplitude, frequancy, phase difference]
        Example
        data = [
            [1, 3, -np.pi/2],
            [4, 7, 0],
            [9, 3, np.pi/4],
            [2, 5, np.pi],
        ]
        ke.getRandomProblem(problem_type = 2, data = data)
        """
        problemTemplate.add_problem(statement.strip(), 'text/html')

        
        soln_s = f"""
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')

        
        problemTemplate.add_answer(svg_str, 'text/svg')

        return problemTemplate
        pass
    
    def _problem_plot_a_circular_fourier_wave(self, *args, **kwargs):
        fourier_helper = self.base_helper.get_fourier_helper()
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        data = kwargs.get('data', [
            [1, 3, 0],
            [7, 2, 0],
            [3, 6, np.pi/4],
        ])
        steps = 1000
        start_theta = kwargs.get('start_theta', 0)
        end_theta = kwargs.get('end_theta', 2*np.pi)
        theta = np.linspace(start_theta, end_theta, steps)
        
        plt.ioff()
        fourier_helper.plot_circular_a_fourier_wave(data, time = theta)
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
        #plt.figure().clear()
        plt.clf()
        plt.close()


        
        statement = f"""
        Plot circular fourier wave.

        ________________________
        You can provide data for different waves [Amplitude, frequancy, phase difference]
        Example
        data = [
            [1, 3, -np.pi/2],
            [4, 7, 0],
            [9, 3, np.pi/4],
            [2, 5, np.pi],
        ]
        ke.getRandomProblem(problem_type = 2, data = data)
        """
        problemTemplate.add_problem(statement.strip(), 'text/html')

        
        soln_s = f"""
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')

        
        problemTemplate.add_answer(svg_str, 'text/svg')

        return problemTemplate
        pass
    
    def _problem_plot_circular_fourier_wave(self, *args, **kwargs):
        fourier_helper = self.base_helper.get_fourier_helper()
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        data = kwargs.get('data', [
            [1, 3, 0],
            [7, 2, 0],
            [3, 6, np.pi/4],
        ])
        steps = 1000
        start_theta = kwargs.get('start_theta', 0)
        end_theta = kwargs.get('end_theta', 2*np.pi)
        theta = np.linspace(start_theta, end_theta, steps)
        
        plt.ioff()
        fourier_helper.plot_circular_fourier_wave(data, time = theta)
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
        #plt.figure().clear()
        plt.clf()
        plt.close()


        
        statement = f"""
        Plot wave and circular fourier waves.

        ________________________
        You can provide data for different waves [Amplitude, frequancy, phase difference]
        Example
        data = [
            [1, 3, -np.pi/2],
            [4, 7, 0],
            [9, 3, np.pi/4],
            [2, 5, np.pi],
        ]
        ke.getRandomProblem(problem_type = 3, data = data)
        """
        problemTemplate.add_problem(statement.strip(), 'text/html')

        
        soln_s = f"""
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')

        
        problemTemplate.add_answer(svg_str, 'text/svg')

        return problemTemplate
        pass
    
    
    def _problem_fourier_expression(self, *args, **kwargs):
        fourier_helper = self.base_helper.get_fourier_helper()
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        result = fourier_helper.get_fourier_expression()
        
        
        problem_s = f"""
        {result['problem_s']}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')

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

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

        return problemTemplate
        pass
    
    def _problem_complex_fourier_expression(self, *args, **kwargs):
        comp_fourier_helper = self.base_helper.get_complex_fourier_helper()
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        result = comp_fourier_helper.get_fourier_expression()
        
        
        problem_s = f"""
        {result['problem_s']}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')

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

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

        return problemTemplate
        pass
    
