'''
Created on 07-Jan-2021

@author: bthakur
'''



'''
Created on 01-Apr-2021
@author: bthakur
'''
import random
import operator
import datetime
import calendar
import numpy as np

from sympy import (symbols, latex, simplify, Eq, sympify,
                   pi, Rational,
                   sin, cos, tan,
                   csc, sec, cot
                   )

from xv.km.util import latex_fraction

from xv.km.kelements import KPrintElement

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


class TimeManager(BaseManager):
    '''
    TimeManager
    Example:
        ke = TimeManager()
        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(TimeManager, cls).__new__(cls, *args, **kwargs)
        for base_class in TimeManager.__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_show_12_hr_clock',
            '_problem_read_12_hr_clock',
            '_problem_time_difference_12_hr_clock',
            '_problem_to_24_hr_clock',
            '_problem_to_12_hr_clock',
            '_problem_to_time_diff_same_day',
            '_problem_to_time_diff_till_end_of_day',
            '_problem_to_time_diff_since_start_of_day',
            '_problem_time_add_delta',
            '_problem_time_deduct_delta',
            '_problem_delta_operations',
            ])
        pass

    def _problem_show_12_hr_clock(self, *args, **kwargs):
        time_helper = self.base_helper.get_common_helper().get_time_helper()

        hours = random.randint(0, 11)
        minutes = random.randint(0, 11) * 5
        seconds = random.randint(0, 11) * 5

        svg_str = time_helper.draw_12_hr_analogue_clock(hours, minutes, seconds)


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

        problem_s = f"""
        Show an analogue clock with time {hours}:{minutes}:{seconds}
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')


        soln_s = f"""
        Time {hours}:{minutes}:{seconds}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str, 'text/svg')

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

        return problemTemplate


    def _problem_read_12_hr_clock(self, *args, **kwargs):
        time_helper = self.base_helper.get_common_helper().get_time_helper()

        hours = random.randint(0, 11)
        minutes = random.randint(0, 59)
        seconds = random.randint(0, 59)

        svg_str = time_helper.draw_12_hr_analogue_clock(hours, minutes, seconds)

        show_needles = ["h"]
        svg_str_h = time_helper.draw_12_hr_analogue_clock(hours, minutes, seconds,
                                                          show_needles = show_needles)
        show_needles = ["m"]
        svg_str_m = time_helper.draw_12_hr_analogue_clock(hours, minutes, seconds,
                                                          show_needles = show_needles)
        show_needles = ["s"]
        svg_str_s = time_helper.draw_12_hr_analogue_clock(hours, minutes, seconds,
                                                          show_needles = show_needles)


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

        problem_s = f"""
        What time the clock is showing?
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        problemTemplate.add_problem(svg_str, 'text/svg')

        soln_s = f"""
        Note which number each needle has passed.
        Note that the number 12 shown on the clock dial can be read as zero also.

        For seconds, multiply every number in the dial by 5 and find the the number it is on or has just passed.
        Seconds = {seconds}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str_s, 'text/svg')


        soln_s = f"""
        For minutes, multiply every number in the dial by 5 and find the the number it is on or has just passed.
        Minutes = {minutes}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str_m, 'text/svg')


        soln_s = f"""
        For hours, we need to just fund the number it is on or has just passed.
        Hours = {hours}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')
        problemTemplate.add_solution(svg_str_h, 'text/svg')


        soln_s = f"""
        Thus, the time the clock is showing is
        {hours}:{minutes}:{seconds}
        """

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

        answer_s = f"""
        {hours}:{minutes}:{seconds}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')

        return problemTemplate

    def _problem_time_difference_12_hr_clock(self, *args, **kwargs):
        time_helper = self.base_helper.get_common_helper().get_time_helper()

        hours1 = random.randint(0, 7)
        minutes1 = random.randint(0, 59)
        seconds1 = random.randint(0, 59)

        hours2 = random.randint(hours1 + 1, 11)
        minutes2 = random.randint(0, 59)
        seconds2 = random.randint(0, 59)

        hours_delta = hours2 - hours1
        minutes_delta = minutes2 - minutes1
        seconds_delta = seconds2 - seconds1


        svg_str1 = time_helper.draw_12_hr_analogue_clock(hours1, minutes1, seconds1)
        svg_str2 = time_helper.draw_12_hr_analogue_clock(hours2, minutes2, seconds2)


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

        problem_s = f"""
        What is the time difference between the two clocks:
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')
        problemTemplate.add_problem(svg_str1, 'text/svg')
        problemTemplate.add_problem(svg_str2, 'text/svg')


        soln_s = f"""
        Time in the second clock and the first clock are:
        $\\displaystyle \\large {{
        {latex(hours2).zfill(2)}:{latex(minutes2).zfill(2)}:{latex(seconds2).zfill(2)} \\\\
        {latex(hours1).zfill(2)}:{latex(minutes1).zfill(2)}:{latex(seconds1).zfill(2)}
        }}
        $

        Deduct hours, minutes and seconds separately.
        $\\displaystyle \\large {{
        {latex(hours_delta).zfill(2)}:{latex(minutes_delta).zfill(2)}:{latex(seconds_delta).zfill(2)}
        }}
        $
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        if seconds_delta < 0:
            soln_s = f"""
            As seconds are in negative, we will borrow one minute from minutes.
            It will increase the seconds by 60.
            $\\displaystyle \\large {{

            = {latex(hours_delta).zfill(2)}:
            {latex(minutes_delta).zfill(2)} - 1:
            {latex(seconds_delta).zfill(2)} + 60 \\\\
            = {latex(hours_delta).zfill(2)}:
            {latex(minutes_delta - 1).zfill(2)}:
            {latex(seconds_delta + 60).zfill(2)}
            }}
            $
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')

            minutes_delta = minutes_delta - 1
            seconds_delta = seconds_delta + 60
            pass

        if minutes_delta < 0:
            soln_s = f"""
            As minutes are in negative, we will borrow one hour from hours.
            It will increase the minutes by 60.
            $\\displaystyle \\large {{

            = {latex(hours_delta).zfill(2)} - 1:
            {latex(minutes_delta).zfill(2)} + 60:
            {latex(seconds_delta).zfill(2)} \\\\
            = {latex(hours_delta - 1).zfill(2)}:
            {latex(minutes_delta + 60).zfill(2)}:
            {latex(seconds_delta).zfill(2)}
            }}
            $
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')

            hours_delta = hours_delta - 1
            minutes_delta = minutes_delta + 60
            pass

        answer_s = f"""
        {hours_delta}:{minutes_delta}:{seconds_delta}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')

        return problemTemplate

    def _problem_to_24_hr_clock(self, *args, **kwargs):
        time_helper = self.base_helper.get_common_helper().get_time_helper()

        t0 = time_helper.get_random_custom_time()
        soln_explanation = t0.get_12_hr_explanation()


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

        problem_s = f"""
        Convert {t0} into 12-hour format.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')


        soln_s = f"""
        {soln_explanation}
        {t0} = {t0.twelve_hr_format}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

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

        return problemTemplate

    def _problem_to_12_hr_clock(self, *args, **kwargs):
        time_helper = self.base_helper.get_common_helper().get_time_helper()

        t0 = time_helper.get_random_custom_time()
        soln_explanation = t0.get_24_hr_explanation()


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

        problem_s = f"""
        Convert {t0.twelve_hr_format} into 24-hour format.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')


        soln_s = f"""
        {soln_explanation}
        {t0.twelve_hr_format} = {t0}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

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

        return problemTemplate


    def _problem_to_time_diff_same_day(self, *args, **kwargs):
        time_helper = self.base_helper.get_common_helper().get_time_helper()

        t0 = time_helper.get_random_custom_time()
        t1 = time_helper.get_random_custom_time()

        t0_str = random.choice([f"{t0.twelve_hr_format}", f"{t0}"])
        t1_str = random.choice([f"{t1.twelve_hr_format}", f"{t1}"])

        if t0 <= t1:
            tmin, tmax = (t0, t1)
        else:
            tmin, tmax = (t1, t0)

        delta = tmax.get_delta(tmin)
        delta_time, conversion_statements = time_helper.delta_to_valid_time_format(*delta)

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

        prob_stmts = []

        tmin_str = random.choice([f"{tmin.twelve_hr_format}", f"{tmin}"])
        tmax_str = random.choice([f"{tmax.twelve_hr_format}", f"{tmax}"])

        prob_stmt = f"""
        A game began at {tmin_str} and ended at {tmax_str}. How long did the game last?
        """
        prob_stmts.append(prob_stmt.strip())

        prob_stmt = f"""
        Find the time difference between {t0_str} and {t1_str}.
        """
        prob_stmts.append(prob_stmt.strip())


        prob_s = random.choice(prob_stmts)

        problemTemplate.add_problem(prob_s.strip(), 'text/html')


        soln_s = f"""
        In 24-hour format:
        {t0_str} = {t0}
        {t1_str} = {t1}

        Time difference, write the later time first

        = {tmax} - {tmin}

        = {tmax.get_delta_str(tmin)}

        = {delta_time}

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

        if conversion_statements:
            soln_s = f"""
            <b>Note: We converted negative values:</b>
            {conversion_statements}
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
            pass

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

        return problemTemplate


    def _problem_to_time_diff_till_end_of_day(self, *args, **kwargs):
        time_helper = self.base_helper.get_common_helper().get_time_helper()

        t0 = time_helper.get_random_custom_time()
        t1 = time_helper.get_custom_time(24, 0, 0)

        t0_str = random.choice([f"{t0.twelve_hr_format}", f"{t0}"])
        t1_str = f"{t1}"

        if t0 <= t1:
            tmin, tmax = (t0, t1)
        else:
            tmin, tmax = (t1, t0)

        delta = tmax.get_delta(tmin)
        delta_time, conversion_statements = time_helper.delta_to_valid_time_format(*delta)


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

        problem_s = f"""
        Find the time difference between {t0_str} and end of the day.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')


        soln_s = f"""
        End of the same day = {t1_str}

        In 24-hour format:
        {t0_str} = {t0}
        {t1_str} = {t1}

        Time difference, write the later time first

        = {tmax} - {tmin}

        = {tmax.get_delta_str(tmin)}

        = {delta_time}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        if conversion_statements:
            soln_s = f"""
            <b>Note: We converted negative values:</b>
            {conversion_statements}
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
            pass

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

        return problemTemplate

    def _problem_to_time_diff_since_start_of_day(self, *args, **kwargs):
        time_helper = self.base_helper.get_common_helper().get_time_helper()

        t0 = time_helper.get_random_custom_time()
        t1 = time_helper.get_custom_time(0, 0, 0)

        t0_str = random.choice([f"{t0.twelve_hr_format}", f"{t0}"])
        t1_str = f"{t1}"

        if t0 <= t1:
            tmin, tmax = (t0, t1)
        else:
            tmin, tmax = (t1, t0)

        delta = tmax.get_delta(tmin)
        delta_time, conversion_statements = time_helper.delta_to_valid_time_format(*delta)


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

        problem_s = f"""
        Find the time difference between {t0_str} and start of the day.
        """
        problemTemplate.add_problem(problem_s.strip(), 'text/html')


        soln_s = f"""
        Start of the same day = {t1_str}

        In 24-hour format:
        {t0_str} = {t0}
        {t1_str} = {t1}

        Time difference, write the later time first

        = {tmax} - {tmin}

        = {tmax.get_delta_str(tmin)}

        = {delta_time}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        if conversion_statements:
            soln_s = f"""
            <b>Note: We converted negative values:</b>
            {conversion_statements}
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
            pass

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

        return problemTemplate


    def _problem_time_add_delta(self, *args, **kwargs):
        time_helper = self.base_helper.get_common_helper().get_time_helper()

        t0 = time_helper.get_random_custom_time()

        while True:
            h = random.choice([0, random.randint(0, 24 - t0.h - 1)])
            m = random.choice([0, random.randint(0, 60 - 1)])
            s = random.choice([0, random.randint(0, 60 - 1)])
            if h + m + s > 0:
                break

        delta_str = t0.to_delta_str([h, m, s])
        delta_time = time_helper.get_custom_time(h = h, m = m, s = s)

        t0_str = random.choice([f"{t0.twelve_hr_format}", f"{t0}"])

        t0_with_delta = t0.add_delta(h = h, m = m, s = s)
        t0_with_delta_str = t0.to_delta_str(t0_with_delta)

        t1, conversion_statements = time_helper.delta_to_valid_time_format(*t0_with_delta)

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

        problem_s1 = f"""
        What will be time {delta_str} after {t0_str}?
        """
        problem_s2 = f"""
        Add {delta_str} to {t0_str}.
        """

        problem_s3 = f"""
        Add {t0_str} and {delta_str}.
        """

        problem_s = random.choice([problem_s1,
                                   problem_s2,
                                   problem_s3])
        problemTemplate.add_problem(problem_s.strip(), 'text/html')


        soln_s = f"""
        In 24-hour format:
        {t0_str} = {t0}

        Time {delta_str} after {t0_str}

        = {t0} + {delta_time}

        = {t0_with_delta_str}

        = {t1}

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

        if conversion_statements:
            soln_s = f"""
            <b>Note: We converted negative or invalid values:</b>
            {conversion_statements}
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
            pass

        #get_add_conversion_statements

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

        return problemTemplate


    def _problem_time_deduct_delta(self, *args, **kwargs):
        time_helper = self.base_helper.get_common_helper().get_time_helper()

        t0 = time_helper.get_random_custom_time()

        while True:
            h = random.choice([0, random.randint(0, t0.h - 1)])
            m = random.choice([0, random.randint(0, 60 - 1)])
            s = random.choice([0, random.randint(0, 60 - 1)])
            if h + m + s > 0:
                break

        delta_str = t0.to_delta_str([h, m, s])
        delta_time = time_helper.get_custom_time(h = h, m = m, s = s)

        t0_str = random.choice([f"{t0.twelve_hr_format}", f"{t0}"])

        t0_with_delta = t0.add_delta(h = -h, m = -m, s = -s)
        t0_with_delta_str = t0.to_delta_str(t0_with_delta)

        t1, conversion_statements = time_helper.delta_to_valid_time_format(*t0_with_delta)

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

        problem_s1 = f"""
        What will be time {delta_str} earlier to {t0_str}?
        """
        problem_s2 = f"""
        Subtract {delta_str} from {t0_str}.
        """

        problem_s3 = f"""
        Deduct {delta_str} from {t0_str}.
        """

        problem_s = random.choice([problem_s1,
                                   problem_s2,
                                   problem_s3])
        problemTemplate.add_problem(problem_s.strip(), 'text/html')


        soln_s = f"""
        In 24-hour format:
        {t0_str} = {t0}

        Time {delta_str} earlier to {t0_str}

        = {t0} - {delta_time}

        = {t0_with_delta_str}

        = {t1}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        if conversion_statements:
            soln_s = f"""
            <b>Note: We converted negative or invalid values:</b>
            {conversion_statements}
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
            pass

        #get_add_conversion_statements

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

        return problemTemplate

    def _problem_delta_operations(self, *args, **kwargs):
        time_helper = self.base_helper.get_common_helper().get_time_helper()

        hms_count = random.randint(2, 5)

        hms_exprs_str = []
        hms_exprs_pos = []
        hms_exprs_neg = []
        while (not hms_exprs_pos or
               not hms_exprs_neg or
               len(hms_exprs_pos) + len(hms_exprs_neg) < hms_count
               ):
            h, m, s = time_helper.get_random_hms()
            hms_str = time_helper.to_hms_str([h, m, s])
            if random.choice([True, False]):
                #add
                hms_exprs_str.append(f" + ({hms_str})")
                hms_exprs_pos.append([h, m, s])
            else:
                #subtract
                hms_exprs_str.append(f" - ({hms_str})")
                hms_exprs_neg.append([h, m, s])
            pass

        hms_exprs_str = "\n".join(hms_exprs_str)

        hms_exprs_pos = np.array(hms_exprs_pos).sum(axis = 0)
        hms_exprs_neg = np.array(hms_exprs_neg).sum(axis = 0)

        hms_exprs_pos_str = time_helper.to_hms_str(hms_exprs_pos)
        hms_exprs_neg_str = time_helper.to_hms_str(hms_exprs_neg)
        subtot_str = f" + ({hms_exprs_pos_str}) - ({hms_exprs_neg_str})"

        hms_sum = hms_exprs_pos - hms_exprs_neg
        hms_sum_str = time_helper.to_hms_str(hms_sum)

        sum_time, conversion_statements = time_helper.delta_to_valid_time_format(*hms_sum)

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

        problem_s = f"""
        Compute:

        {hms_exprs_str}

        """

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


        soln_s = f"""
        {hms_exprs_str}

        = {subtot_str}

        = {hms_sum_str}

        = {sum_time}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        if conversion_statements:
            soln_s = f"""
            <b>Note: We converted negative or invalid values:</b>
            {conversion_statements}
            """
            problemTemplate.add_solution(soln_s.strip(), 'text/html')
            pass

        #get_add_conversion_statements

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

        return problemTemplate

