'''
Created on 10-Jun-2021

@author: bthakur
'''
from xv.util import toIntOrFloat
from sympy import (symbols, Rational, sympify, pi, E, I, 
                   latex, solve, Eq, simplify,
                   sqrt,
                   UnevaluatedExpr as ueval
                   )
import sympy

import random
from xv.km.util import latex_fraction
import operator
import inspect
import traceback

# _number_system_example_helper
# NumberSystemExampleHelper

# _number_system_helper

from xv.util import UtilClass

class NumberSystemExampleHelper(UtilClass):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        pass



    def get_simple_examples_by_ns_type(self, ns_type, count = None):
        numbers = self.get_simple_examples()
        numbers_by_ns_type = numbers.get(ns_type, None)
        
        if numbers_by_ns_type is None:
            return None
        
        if count is None:
            return numbers_by_ns_type
        
        if len(numbers_by_ns_type) <= count:
            return numbers_by_ns_type
        
        random_numbers = {}
        unique_values = set()
        
        keys = list(numbers_by_ns_type.keys())
        random.shuffle(keys)
        
        for k in keys:
            v = numbers_by_ns_type[k]
            if v in unique_values:
                continue
            
            unique_values.add(v)
            random_numbers[k] = v
            pass
        
        return random_numbers
        pass
    
    def get_simple_examples(self):
        natural_numbers = list(range(1, 10))
        whole_numbers = list(range(0, 10))
        integer_numbers = list(range(-9, 10))
        
        rational_numbers = set()
        while (len(rational_numbers) < 9):
            rational_numbers.add(Rational(random.randint(0, 9), random.randint(1, 9)))
            pass
        rational_numbers = list(rational_numbers)
        
        irrational_numbers = [sqrt(ueval(x)) for x in range(0, 10) 
                              if sqrt(x) != int(sqrt(x))]
        
        real_numbers = []
        real_numbers.extend(rational_numbers)
        real_numbers.extend(irrational_numbers)
        real_numbers_set = set([float(simplify(x)) for x in real_numbers])
        
        numbers = {}
        numbers['natural'] = {x:x for x in natural_numbers}
        numbers['whole'] = {x:x for x in whole_numbers}
        numbers['integer'] = {x:x for x in integer_numbers}
        numbers['rational'] = {x:x for x in rational_numbers}
        numbers['irrational'] = {x:float(simplify(x)) for x in irrational_numbers}
        numbers['real'] = {x:float(simplify(x)) for x in real_numbers}

        
        return numbers
        pass
    

    def get_examples_qa_text(self, ns_type, n_vals):
        real_number_props = []
        
        real_number_props.append(self._get_prop_item_a_b_c(ns_type, n_vals))
        real_number_props.append(self._get_prop_item_a_gt_b_gt_c(ns_type, n_vals))
        real_number_props.append(self._get_prop_item_a_gt_b(ns_type, n_vals))

        if ns_type in  ['integer', 'rational', 'irrational', 'real']:
            temp_data = self._get_prop_item_a_gt_b_negative_c(ns_type, n_vals)
            if temp_data:
                real_number_props.append(temp_data)
            pass

        identifier = f"{self.__class__.__name__}{id(self)}.{inspect.stack()[0][3]}_real_number_props"
        qa_text = self.get_no_repeat_random_choice(identifier, real_number_props)
        return qa_text

    def _get_prop_item_a_b_c(self, ns_type, n_vals):
        '''
        a , b , c
        '''
        
        n0, n1, n2 = symbols('a, b, c')

        n_keys = [k for k, v in n_vals.items()]

        n_sorted_vals = [k for k, v in sorted(n_vals.items(), 
                                              key=lambda item: item[1])]

        n_rev_sorted_vals = [k for k, v in sorted(n_vals.items(), 
                                                  key=lambda item: item[1],
                                                  reverse = True)]


        q_text = f"""
        ${n0}$, ${n1}$ and ${n2}$ are {ns_type} numbers.
        $\\displaystyle
        \\begin{{align}}
        \\text{{Give examples of  }} \\\\
        & {n0} \\neq {n2} \\\\ 
        & {n0} < {n2} \\\\ 
        & {n0} > {n2}
        \\end{{align}} 
        $
        """
        
        a_text = f"""
        $\\displaystyle
        \\begin{{align}}
        \\text{{Let }}
        & {n0} = {latex(n_rev_sorted_vals[0])} \\\\ 
        & {n1} = {latex(n_rev_sorted_vals[1])} \\\\ 
        & {n2} = {latex(n_rev_sorted_vals[2])} 
        \\end{{align}} 
        $
        $\\displaystyle
        \\begin{{align}}
        \\text{{Then }} \\\\
        & {latex(n_rev_sorted_vals[0])} \\neq {latex(n_rev_sorted_vals[2])} \\\\ 
        & {latex(n_rev_sorted_vals[1])} < {latex(n_rev_sorted_vals[0])} \\\\ 
        & {latex(n_rev_sorted_vals[1])} > {latex(n_rev_sorted_vals[2])} 
        \\end{{align}} 
        $
        """
        prop_item = {
            "ns_type": ns_type,
            "n_vals": n_vals,
            "vars": [n0, n1, n2],
            "q_text": q_text.strip(),
            "a_text": a_text.strip()
            }
        return prop_item

        pass

    def _get_prop_item_a_gt_b_gt_c(self, ns_type, n_vals):
        '''
        a > b > c
        '''
        
        n0, n1, n2 = symbols('a, b, c')

        n_keys = [k for k, v in n_vals.items()]

        n_sorted_vals = [k for k, v in sorted(n_vals.items(), 
                                              key=lambda item: item[1])]

        n_rev_sorted_vals = [k for k, v in sorted(n_vals.items(), 
                                                  key=lambda item: item[1],
                                                  reverse = True)]


        q_text = f"""
        ${n0}$, ${n1}$ and ${n2}$ are {ns_type} numbers.
        $\\displaystyle
        \\begin{{align}}
        & \\text{{If {n0} > {n1} and {n1} > {n2}, then give examples of  }} \\\\
        & {n0} = {n2} \\\\
        & {n0} \\neq {n2} \\\\ 
        & {n0} < {n2} \\\\ 
        & {n0} > {n2}
        \\end{{align}} 
        $
        """
        
        a_text = f"""
        $\\displaystyle
        \\begin{{align}}
        \\text{{Let }}
        & {n0} = {latex(n_rev_sorted_vals[0])} \\\\ 
        & {n1} = {latex(n_rev_sorted_vals[1])} \\\\ 
        & {n2} = {latex(n_rev_sorted_vals[2])} 
        \\end{{align}} 
        $

        $\\displaystyle
        \\begin{{align}}
        & \\text{{So that {latex(n_rev_sorted_vals[0])} > {latex(n_rev_sorted_vals[1])} 
            and {latex(n_rev_sorted_vals[1])} > {latex(n_rev_sorted_vals[2])}, then }} \\\\
        & {n0} = {n2} \\text{{ is not possible.}} \\\\
        & {latex(n_rev_sorted_vals[0])} \\neq {latex(n_rev_sorted_vals[2])} \\\\ 
        & {n0} < {n2}  \\text{{ is not possible.}} \\\\ 
        & {latex(n_rev_sorted_vals[0])} > {latex(n_rev_sorted_vals[2])} 
        \\end{{align}} 
        $
        """
        prop_item = {
            "ns_type": ns_type,
            "n_vals": n_vals,
            "vars": [n0, n1, n2],
            "q_text": q_text.strip(),
            "a_text": a_text.strip()
            }
        return prop_item

        pass

    def _get_prop_item_a_gt_b(self, ns_type, n_vals):
        '''
        a > b and c >= 0
        '''
        
        n0, n1, n2 = symbols('a, b, c')

        n_keys = [k for k, v in n_vals.items()]

        n_sorted_vals = [k for k, v in sorted(n_vals.items(), 
                                              key=lambda item: item[1])]

        n_rev_sorted_vals = [k for k, v in sorted(n_vals.items(), 
                                                  key=lambda item: item[1],
                                                  reverse = True)]


        q_text = f"""
        ${n0}$, ${n1}$ and ${n2}$ are {ns_type} numbers.
        $\\displaystyle
        \\begin{{align}}
        & \\text{{If {n0} > {n1}, then give examples of  }} \\\\
        & {n1} < {n0} \\\\
        & -{n0} > -{n1} \\\\
        & -{n0} < -{n1} \\\\
        & {n0} \\pm {n2} > {n1} \\pm {n2} \\\\
        & {n0} \\cdot {n2} > {n1} \\cdot {n2} \\\\
        & {n0} \\div {n2} > {n1} \\div {n2} \\\\
        
        \\end{{align}} 
        $
        """
        
        a_text = f"""
        $\\displaystyle
        \\begin{{align}}
        \\text{{Let }}
        & {n0} = {latex(n_rev_sorted_vals[0])} \\\\ 
        & {n1} = {latex(n_rev_sorted_vals[1])} \\\\ 
        & {n2} = {latex(n_rev_sorted_vals[2])} 
        \\end{{align}} 
        $
        $\\displaystyle
        \\begin{{align}}
        \\text{{So that {latex(n_rev_sorted_vals[0])} > {latex(n_rev_sorted_vals[1])}, then }} \\\\
        & {latex(n_rev_sorted_vals[1])} < {latex(n_rev_sorted_vals[0])} \\\\ 
        & -{n0} > -{n1} \\text{{ is not possible.}} \\\\
        & -{latex(n_rev_sorted_vals[0])} < -{latex(n_rev_sorted_vals[1])} \\\\ 
        
        & {latex(n_rev_sorted_vals[0])} \\pm {latex(n_rev_sorted_vals[2])} 
        > {latex(n_rev_sorted_vals[1])} \\pm {latex(n_rev_sorted_vals[2])} \\\\
        
        & {latex(n_rev_sorted_vals[0])} \\cdot {latex(n_rev_sorted_vals[2])} 
        > {latex(n_rev_sorted_vals[1])} \\cdot {latex(n_rev_sorted_vals[2])} \\\\
        
        & {latex(n_rev_sorted_vals[0])} \\div {latex(n_rev_sorted_vals[2])} 
        > {latex(n_rev_sorted_vals[1])} \\div {latex(n_rev_sorted_vals[2])} \\\\
        
        \\end{{align}} 
        $
        """
        prop_item = {
            "ns_type": ns_type,
            "n_vals": n_vals,
            "vars": [n0, n1, n2],
            "q_text": q_text.strip(),
            "a_text": a_text.strip()
            }
        return prop_item

        pass


    def _get_prop_item_a_gt_b_negative_c(self, ns_type, n_vals):
        '''
        a > b and c < 0
        '''
        
        n0, n1, n2 = symbols('a, b, c')

        n_keys = [k for k, v in n_vals.items()]

        n_sorted_vals = [k for k, v in sorted(n_vals.items(), 
                                              key=lambda item: item[1])]

        n_rev_sorted_vals = [k for k, v in sorted(n_vals.items(), 
                                                  key=lambda item: item[1],
                                                  reverse = True)]
        
        if float(simplify(n_rev_sorted_vals[2])) > 0:
            n_rev_sorted_vals[2] = - n_rev_sorted_vals[2]
            
        print(f"""
        n_rev_sorted_vals[2] = {n_rev_sorted_vals[2]}
        """)

        q_text = f"""
        ${n0}$, ${n1}$ and ${n2}$ are {ns_type} numbers.
        $\\displaystyle
        \\begin{{align}}
        & \\text{{If {n0} > {n1}, then give examples of  }} \\\\
        & {n1} < {n0} \\\\
        & -{n0} > -{n1} \\\\
        & -{n0} < -{n1} \\\\
        & {n0} \\pm {n2} > {n1} \\pm {n2} \\\\
        & {n0} \\cdot {n2} < {n1} \\cdot {n2} \\\\
        & {n0} \\div {n2} < {n1} \\div {n2} \\\\
        
        \\end{{align}} 
        $
        """
        
        a_text = f"""
        $\\displaystyle
        \\begin{{align}}
        \\text{{Let }}
        & {n0} = {latex(n_rev_sorted_vals[0])} \\\\ 
        & {n1} = {latex(n_rev_sorted_vals[1])} \\\\ 
        & {n2} = {latex(n_rev_sorted_vals[2])} 
        \\end{{align}} 
        $
        $\\displaystyle
        \\begin{{align}}
        \\text{{So that {latex(n_rev_sorted_vals[0])} > {latex(n_rev_sorted_vals[1])}, then }} \\\\
        & {latex(n_rev_sorted_vals[1])} < {latex(n_rev_sorted_vals[0])} \\\\ 
        & -{n0} > -{n1} \\text{{ is not possible.}} \\\\
        & -{latex(n_rev_sorted_vals[0])} < -{latex(n_rev_sorted_vals[1])} \\\\ 
        
        & {latex(n_rev_sorted_vals[0])} \\pm {latex(n_rev_sorted_vals[2])} 
        > {latex(n_rev_sorted_vals[1])} \\pm {latex(n_rev_sorted_vals[2])} \\\\
        
        & {latex(n_rev_sorted_vals[0])} \\cdot {latex(n_rev_sorted_vals[2])} 
        < {latex(n_rev_sorted_vals[1])} \\cdot {latex(n_rev_sorted_vals[2])} \\\\
        
        & {latex(n_rev_sorted_vals[0])} \\div {latex(n_rev_sorted_vals[2])} 
        < {latex(n_rev_sorted_vals[1])} \\div {latex(n_rev_sorted_vals[2])} \\\\
        
        \\end{{align}} 
        $
        """
        prop_item = {
            "ns_type": ns_type,
            "n_vals": n_vals,
            "vars": [n0, n1, n2],
            "q_text": q_text.strip(),
            "a_text": a_text.strip()
            }
        return prop_item

        pass

    pass
#class NumberSystemExampleHelper