'''
Created on 01-Apr-2021
@author: bthakur
'''
from functools import reduce

import numpy as np
import random
import inspect
import traceback
from sympy import (simplify, sympify, log, symbols, Eq, Matrix, latex, 
                   solve, Rational, diag)


from xv.km.util import latex_fraction
from xv.util import list_to_str, number_to_nth

        


from ..helper import BaseHelper
from xv.km.managers import BaseManager
from xv.km.kelements import KProblemTemplate
from sympy.core.expr import UnevaluatedExpr
        
# _data_handling_manager
# DataHandlingManager
        
class DataHandlingManager(BaseManager):
    '''
    DataHandlingManager
    Example:
        ke = DataHandlingManager()
        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(DataHandlingManager, cls).__new__(cls, *args, **kwargs)
        for base_class in DataHandlingManager.__bases__:
            base_class.__init__(instance, *args, **kwargs)
        return instance
    
    def __init__(self, *args, **kwargs):
        self.base_helper = BaseHelper(**kwargs)
        self._equation = None
        pass
    
    

    def _init_problem_types(self, *args, **kwargs):
        self._append_problem_types([
            '_problem_random_data_counting',
            '_problem_find_closest_pair',
            '_problem_forecasting',
            
            ]) 
        pass

    def _problem_random_data_counting(self, *args, **kwargs):
        
        ea_helper = self.base_helper.get_stats_helper().get_data_handling_helper()
        data_set = ea_helper.get_random_data_set(category = 'single_digit')
        result = ea_helper.analyze_data(data_set)
        
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        We have this data:
        {result['data_set_str']}

        Based on the above data, answer the following questions:
        {result['questions_str']}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        {result['qas_str']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        {result['answers_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        
    def _problem_find_closest_pair(self, *args, **kwargs):
        ea_helper = self.base_helper.get_stats_helper().get_data_handling_helper()
        
        size = random.randint(3, 10)
        data_set_count = random.randint(3, 4)
        
        data_sets = []
        for _ in range(data_set_count):
            data_set = ea_helper.get_random_series(category = 'single_digit', size = size)
            data_sets.append(data_set) 
        
        data_labels = list('ABCDE')[:len(data_sets)]
        result = ea_helper.get_closest_pair(data_sets, data_labels = data_labels)
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        We have this data sets:
        
        {result['data_sets_str']}

        Find the closest pair among the data sets.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        
        soln_s = f"""
        <h3>Let us calculate the deviation of each possible pair:</h3>
        {result['deviation_html_tables']}
        
        <h3>Let us summarize the above results:</h3>
        {result['deviation_summary_table']}
        
        The minimum absolute deviation = {result['min_abs_devation']}.
        Hence, according to minimum absolute deviation, the closest pairs = {result['closet_by_abs_devation_str']}
        
        Similarly, minimum square deviation = {result['min_sqr_devation']}.
        Hence, according to minimum square deviation, the closest pairs = {result['closet_by_sqr_devation_str']}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        
        answer_s = f"""
        The closest pairs by absolute deviation = {result['closet_by_abs_devation_str']}

        The closest pairs by square deviation = {result['closet_by_sqr_devation_str']}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
        

    def _problem_forecasting(self, *args, **kwargs):
        
        ea_helper = self.base_helper.get_stats_helper().get_data_handling_helper()
        
        result = ea_helper.get_random_forcasting()
        svg_str = result['svg_str']
        svg_str_annotated = result['svg_str_annotated']
        
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        problem_s = f"""
        Answer the questions based on forecast map below:
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        problemTemplate.add_problem(svg_str, 'text/svg')
        

        problem_s = f"""
        {result['questions_str']}
        """
        
        soln_s = f"""
        Let us redraw the graph.
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        problemTemplate.add_solution(svg_str_annotated, 'text/svg')

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

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

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

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