'''
Created on 01-Apr-2021
@author: bthakur
'''
import random

from sympy import symbols, latex, simplify

from xv.km.util import latex_fraction


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

#_arithmetic_series_manager
# ArithmeticSeriesManager

class ArithmeticSeriesManager(BaseManager):
    '''
    ArithmeticSeriesManager
    Example:
        ke = ArithmeticSeriesManager()
        ke.getRandomProblem()
        ke.getRandomProblem(problem_type = 0)
        ...

        ke.printProblem()
        ke.printAnswer()
        ke.printSolution()

        ke.printProblemTypes()

        Optional parameters with most problem types:
        first_term = None  #sympy or numeric expression
        common_difference = None #sympy or numeric expression

    '''
    _OPR_TYPE_COUNT = 10

    def __new__(cls, *args, **kwargs):
        instance = super(ArithmeticSeriesManager, cls).__new__(cls, *args, **kwargs)
        for base_class in ArithmeticSeriesManager.__bases__:
            base_class.__init__(instance, *args, **kwargs)
        return instance

    def __init__(self, *args, **kwargs):
        self.base_helper = BaseHelper(**kwargs)

        self._arithmetic_series_system = None

        pass


    def _init_problem_types(self, *args, **kwargs):
        self._append_problem_types([
            '_problem_next_terms',
            '_problem_write_expression_for_as',
            '_problem_write_expression_for_gs_simple',
            '_problem_write_expression_for_gs',
            '_problem_term_in_a_simple_gs',
            '_problem_term_in_a_gs',
            '_problem_write_expression_for_gs_sum_simple',
            '_problem_write_expression_for_gs_sum',
            '_problem_find_series_props',
            '_problem_arithmetic_series_system_random_problem',
            ])
        pass

    def _problem_next_terms(self, *args, **kwargs):
        as_helper = self.base_helper.get_algebra_helper().get_arithmetic_series_helper()


        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        a = random.randint(1, 10)
        r = random.randint(1, 10)
        n = random.randint(5, 10)
        n1 = 4

        gs = as_helper.get_random_series(a = a, r = r, n = n)
        gs_n1 = as_helper.get_random_series(a = a, r = r, n = n1)

        result = gs.get_series_computation_stmt()
        result_n1 = gs_n1.get_series_computation_stmt()

        problem_s = f"""
        Write ${latex(gs.n)}$ terms of a series whose first ${latex(n1)}$ terms are

        $\\displaystyle {result_n1['computed_terms_str'].strip()}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)

        r_latex = gs.get_var_latex(gs.r)
        n_latex = gs.get_var_latex(gs.n)

        soln_s = f"""
        {as_helper.get_term_formula_statement()}

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

        answer_s = f"""
        $\\displaystyle {result['computed_terms_str'].strip()}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')

        return problemTemplate


    def _problem_write_expression_for_as(self, *args, **kwargs):

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        t0 = random.choice([0, symbols('a')])
        t = symbols('t')
        d = symbols('d')
        n = symbols('n')

        t0 = random.choice([t0, random.randint(1, 9)])
        t = random.choice([t, random.randint(1, 9)])
        d = random.choice([d, random.randint(1, 9)])
        n = random.choice([n, random.randint(1, 9)])

        terms = [t0]
        t_latexes = [f"${t}_{0} = {latex(t0)}$"]

        for i in [1, 2, 3, n]:
            prevTerm = terms[-1]
            if i == n:
                prevTerm = t0 + d * (n-1)
            t_latex = f"""
            ${t}_{{{i}}} = {latex(prevTerm)} + {d} = {latex(t0 + d * i)} $
            """
            terms.append(simplify(prevTerm + d))
            t_latexes.append(t_latex.strip())
            pass

        t_latexes.insert(-1, r"$\cdots$")
        if t0 == 0:
            terms_str = [f"${latex(term)}$" for term in terms ]
        else:
            terms_str = [f"$({latex(term)})$" for term in terms ]

        terms_str.insert(-1, r"$\cdots$")

        t_latexes_str = "\n".join(t_latexes)
        series_str = " + ".join(terms_str)

        problem_s = f"""
        Write arithmetic series of ${n + 1}$ terms,
        with first term ($t_0$) as ${t0}$ and the common difference as ${d}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)

        soln_s = f"""
        Add {d} to the previous terms with to get next term
        {t_latexes_str}

        So, the series is
        {series_str}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

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

        return problemTemplate

    def _problem_write_expression_for_gs_simple(self, *args, **kwargs):
        as_helper = self.base_helper.get_algebra_helper().get_arithmetic_series_helper()


        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        gs = as_helper.get_standard_series(a = random.choice([1, symbols('a')]))

        result = gs.get_series_computation_stmt()

        problem_s = f"""
        Write arithmetic series of ${latex(gs.n)}$ terms,
        with first term ($t_0$) ($t_0$) as ${latex(gs.a)}$ and the common difference as ${latex(gs.r)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)

        r_latex = gs.get_var_latex(gs.r)
        n_latex = gs.get_var_latex(gs.n)

        soln_s = f"""
        {as_helper.get_term_formula_statement()}

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

        answer_s = f"""

        $\\displaystyle {result['series_str'].strip()}$

        It can also be written as $\\displaystyle {result['summtion_latex'].strip()}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')

        return problemTemplate

    def _problem_write_expression_for_gs(self, *args, **kwargs):
        as_helper = self.base_helper.get_algebra_helper().get_arithmetic_series_helper()


        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        a = kwargs.get('a', None)
        r = kwargs.get('r', None)
        n = kwargs.get('n', None)

        gs = as_helper.get_random_series(a = a, r = r, n = n)

        result = gs.get_series_computation_stmt()

        problem_s = f"""
        Write arithmetic series of ${latex(gs.n)}$ terms,
        with first term ($t_0$) as ${latex(gs.a)}$ and the common difference as ${latex(gs.r)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)

        r_latex = gs.get_var_latex(gs.r)
        n_latex = gs.get_var_latex(gs.n)

        soln_s = f"""
        {as_helper.get_term_formula_statement()}

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

        answer_s = f"""

        $\\displaystyle {result['series_str'].strip()}$

        It can also be written as $\\displaystyle {result['summtion_latex'].strip()}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')

        return problemTemplate

    def _problem_term_in_a_simple_gs(self, *args, **kwargs):
        '''
        Optional Parameters:
        a = None  #first term ($t_0$); sympy or numeric expression
        r = None #common differencen; sympy or numeric expression
        n = None #index of term; sympy or numeric expression
        '''
        as_helper = self.base_helper.get_algebra_helper().get_arithmetic_series_helper()


        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        a = random.randint(1, 9) * random.choice([1, -1])
        r = random.randint(1, 9) * random.choice([1, -1])
        n = random.randint(10, 99)

        gs = as_helper.get_random_series(a = a, r = r, n = n)

        term_expr =  f"""
        \\text{{ (first term) }} + \\text{{ (common difference) }} \\cdot \\text{{ (term index) }}
        \\qquad \\text{{ Please note that index starts with zero. }}
        """

        term_latex = gs.get_term(gs.n, to_latex =  True, bracket = False)
        term = gs.get_term(gs.n, to_latex = False)

        problem_s = f"""
        Write $t_{{{latex(gs.n)}}}$ or $\\left({latex(gs.n + 1)}\\right)th$ term of the arithmetic series
        whose first term ($t_0$) is ${latex(gs.a)}$ and the common difference as ${latex(gs.r)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)

        soln_s = f"""

        $$\\displaystyle
        \\begin{{aligned}}

        {{t}}_{{{latex(gs.n)}}}
        & = \\left({latex(gs.n + 1)}\\right)\\text{{th term}}
        \\\\ \\\\

        & = {term_expr}
        \\\\ \\\\

        & = {term_latex}
        \\\\ \\\\

        & = {latex(term)}

        \\end{{aligned}}
        $$

        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        answer_s = f"""
        $\\displaystyle {latex(term)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')

        return problemTemplate

    def _problem_term_in_a_gs(self, *args, **kwargs):
        '''
        Optional Parameters:
        a = None  #first term ($t_0$); sympy or numeric expression
        r = None #common differencen; sympy or numeric expression
        n = None #index of term; sympy or numeric expression
        '''
        as_helper = self.base_helper.get_algebra_helper().get_arithmetic_series_helper()


        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        a = kwargs.get('a', None)
        r = kwargs.get('r', None)
        n = kwargs.get('n', None)

        gs = as_helper.get_random_series(a = a, r = r, n = n)

        term_expr =  f"""
        \\text{{ (first term) }} + \\text{{ (common difference) }} \\cdot \\text{{ (term index) }}
        \\qquad \\text{{ Please note that index starts with zero. }}
        """

        term_latex = gs.get_term(gs.n, to_latex =  True, bracket = False)
        term = gs.get_term(gs.n, to_latex = False)

        problem_s = f"""
        Write $t_{{{latex(gs.n)}}}$ or $\\left({latex(gs.n + 1)}\\right)th$ term of the arithmetic series
        whose first term ($t_0$) is ${latex(gs.a)}$ and the common difference as ${latex(gs.r)}$
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)

        soln_s = f"""

        $$\\displaystyle
        \\begin{{aligned}}

        {{t}}_{{{latex(gs.n)}}}
        & = \\left({latex(gs.n + 1)}\\right)\\text{{th term}}
        \\\\ \\\\

        & = {term_expr}
        \\\\ \\\\

        & = {term_latex}
        \\\\ \\\\

        & = {latex(term)}

        \\end{{aligned}}
        $$

        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        answer_s = f"""
        $\\displaystyle {latex(term)}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')

        return problemTemplate


    def _problem_write_expression_for_gs_sum_simple(self, *args, **kwargs):
        as_helper = self.base_helper.get_algebra_helper().get_arithmetic_series_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

        gs = as_helper.get_standard_series(a = random.choice([1, symbols('a')]))
        result = gs.get_series_sum_stmt()

        problem_s1 = f"""
        Write the sum of arithmetic series of ${latex(gs.n)}$ terms,
        with first term ($t_0$) as ${latex(gs.a)}$ and the common difference as ${latex(gs.r)}$
        """
        problem_s2 = f"""
        Solve: $\\displaystyle {result['summtion_latex']}$
        """
        problem_s = random.choice([problem_s1, problem_s2])

        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)

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

        answer_s = f"""
        $\\displaystyle {result['sum_str']}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')

        return problemTemplate

    def _problem_write_expression_for_gs_sum(self, *args, **kwargs):
        as_helper = self.base_helper.get_algebra_helper().get_arithmetic_series_helper()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')


        a = kwargs.get('a', None)
        r = kwargs.get('r', None)
        n = kwargs.get('n', None)

        standard_gs = as_helper.get_standard_series(a = symbols('a'))
        standard_sum_str = standard_gs.get_sum(to_latex = True)

        gs = as_helper.get_random_series(a = a, r = r, n = n)
        result = gs.get_series_sum_stmt()

        problem_s1 = f"""
        Write the sum of arithmetic series of ${latex(gs.n)}$ terms,
        with first term ($t_0$) as ${latex(gs.a)}$ and the common difference as ${latex(gs.r)}$
        """
        problem_s2 = f"""
        Solve: $\\displaystyle {result['summtion_latex']}$
        """
        problem_s = random.choice([problem_s1, problem_s2])

        problemTemplate.add_problem(problem_s.strip(), 'text/html',
                                    add_html_line_break = False)

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

        answer_s = f"""
        $\\displaystyle {result['sum_str']}$
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')

        return problemTemplate

    def _problem_find_series_props(self, *args, **kwargs):

        as_helper = self.base_helper.get_algebra_helper().get_arithmetic_series_helper()
        result = as_helper.get_problem_find_series_props()

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

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

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

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

        return problemTemplate



    def get_arithmetic_series_system(self, *args, **kwargs):
        if self._arithmetic_series_system is None:
            from xv.km.mathsys import MathSystemFactory, ArithmeticSeriesSystem
            math_sys = MathSystemFactory.GET_INSTANCE(ArithmeticSeriesSystem,
                                                      use_cache = False,
                                                      verbose = self.verbose)
            self._arithmetic_series_system = math_sys
            pass

        return self._arithmetic_series_system
        pass


    def _problem_arithmetic_series_system_random_problem(self, *args, **kwargs):

        math_sys = self.get_arithmetic_series_system()

        result = math_sys.get_random_problem(**kwargs)

        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')

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


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

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

        return problemTemplate
