'''
Created on 12-Jul-2021

@author: bthakur
'''
import random
import inspect
import traceback
import re
import operator
import datetime
import calendar
from time import time, ctime

from sympy import (symbols, latex, simplify, nsimplify, 
                   Rational,
                   Eq,
                   pi,
                   sin, cos, tan,
                   csc, sec, cot
                   )
from sympy.abc import theta
import sympy

from xv.km.util import latex_fraction, latex_exponent, wrap_bracket

class ArithmeticWalkHelper:
    def __init__(self, b = None, e = None, count = 2, **kwargs):
        self.verbose = kwargs.get('verbose', False)
        self.implies = symbols(u'\U000021D2')

        if not b is None:
            self.b = b
        else:
            self.b = random.randint(2, 9)
        
        if not e is None:
            self.e = e
        else:
            self.e = random.randint(2, 9)
            
        self.start_types = self._init_start_types()
        
        self.walks = None 
        self._init_random_walks(self.b, count)
        pass

    def _init_start_types(self):
        start_types = [
            (self.b, ['inv', 'exp', 'neg_exp']), 
            (Rational(1, self.b), ['inv', 'exp']), 
            (self.b ** self.e, ['inv', 'inv_exp']),
            (Rational(1, self.b ** self.e), ['inv', 'inv_exp', 'neg_inv_exp']),
          ]
        return start_types
        pass

    def _get_random_walk_type(self, start):
        for start_type in self.start_types:
            if start_type[0] != start:
                continue
            return random.choice(start_type[-1])
        return None
        pass


    def _init_random_walks(self, start, count):
        self.walks = []
        loopCount = 0
        while len(self.walks) < count and loopCount < 10 * count:
            loopCount = loopCount + 1
            sOut = f"""
            start = {start}
            count = {count}
            loopCount = {loopCount}
            len(self.walks) = {len(self.walks)}
            """
            walk = self._get_random_walk(start)
            if walk is None:
                continue
            self.walks.append(walk)
            start = self.walks[-1]['end']

    def _get_random_walk(self, start):
        loopCount = 0
        while loopCount < 10:
            loopCount = loopCount + 1
            
            walk_type = self._get_random_walk_type(start)
            if walk_type is None:
                continue
            
            walk_method_name = f"_get_{walk_type}_walk"
            if not hasattr(self, walk_method_name):
                print(f"Invalid walk_method_name = {walk_method_name}")
                continue
            
            walk_method = getattr(self, walk_method_name)
            
            return walk_method(start)
        
        return None
        pass
        
    def _exists(self, start, end = None):
        if end is None:
            return False
        
        for walk in self.walks:
            if (walk['start'] == start and 
                walk['end'] == end):
                return True
            
        return False
        pass

    def _get_inv_walk(self, start):
        x = start
        x_latex = self.get_var_latex(start)
        x_inv = nsimplify(1/x) 
        end = x_inv
        
        if self._exists(start, end):
            return None
        
        walk = {
            'walk_type': 'inv',
            'start': start,
            'start_latex': f"${x_latex}$",
            'statement': f"From ${x_latex}$, go to ${latex(x_inv)}$", 
            'solution': f"${{{x_latex}}}^{{-1}} = {latex(x_inv)}$ ",
            'answer': f"${{{x_latex}}}^{{-1}}$ ",
            'end': end,
            }
        
        return walk
        
        pass


    def _get_exp_walk(self, start):
        x = start
        x_latex = self.get_var_latex(start)
        end = nsimplify(x ** self.e) 
        
        if self._exists(start, end):
            return None
        
        multiple_latex = latex(" * ".join([f"{x_latex}"] * self.e))
      
        walk = {
            'walk_type': 'exp',
            'start': start,
            'start_latex': f"${x_latex}$",
            'statement': f"From ${x_latex}$, go to ${latex(end)}$", 
            'solution': f"""${{{x_latex}}}^{{{self.e}}} 
                            = {multiple_latex} = {latex(end)}$ """,
            'answer': f"${{{x_latex}}}^{{{self.e}}}$ ",
            'end': end,
            }
        
        return walk
        
        pass

    def _get_inv_exp_walk(self, start):
        x = start
        x_latex = self.get_var_latex(start)
        e_inv = Rational(1, self.e)
        end = nsimplify(x ** e_inv) 
        
        if self._exists(start, end):
            return None
        
      
        walk = {
            'walk_type': 'inv_exp',
            'start': start,
            'start_latex': f"${x_latex}$",
            'statement': f"From ${x_latex}$, go to ${latex(end)}$", 
            'solution': f"""${{{x_latex}}}^{{{latex(e_inv)}}} 
                            = {{\\left({latex(end)}\\right)}}^{{{latex(self.e)} * {latex(e_inv)}}} 
                            = {latex(end)}$ """,
            'answer': f"${{{x_latex}}}^{{{latex(e_inv)}}}$ ",
            'end': end,
            }
        
        return walk
        
        pass
    
    
    
    def _get_neg_inv_exp_walk(self, start):
        x = start
        x_latex = self.get_var_latex(start)
        e_neg_inv = Rational(1, -self.e)
        end = nsimplify(x ** e_neg_inv) 
        
        if self._exists(start, end):
            return None
        
      
        walk = {
            'walk_type': 'inv_exp',
            'start': start,
            'start_latex': f"${x_latex}$",
            'statement': f"From ${x_latex}$, go to ${latex(end)}$", 
            'solution': f"""${{{x_latex}}}^{{{latex(e_neg_inv)}}} 
                            = {{\\left({latex(end)}\\right)}}^{{{latex(self.e)} * {latex(e_neg_inv)}}} 
                            = {latex(end)}$ """,
            'answer': f"${{{x_latex}}}^{{{latex(e_neg_inv)}}}$ ",
            'end': end,
            }
        
        return walk
        
        pass
    

    def _get_neg_exp_walk(self, start):
        x = start
        x_latex = self.get_var_latex(start)
        e_neg = - self.e
        end = nsimplify(x ** e_neg) 
        
        expr_latex = latex_fraction(1, f"{{{x_latex}}}^{{{latex(self.e)}}}")
        
        if self._exists(start, end):
            return None
        
      
        walk = {
            'walk_type': 'inv_exp',
            'start': start,
            'start_latex': f"${x_latex}$",
            'statement': f"From ${x_latex}$, go to ${latex(end)}$", 
            'solution': f"""${{{x_latex}}}^{{{latex(e_neg)}}} 
                            = {expr_latex} 
                            = {latex(end)}$ """,
            'answer': f"${{{x_latex}}}^{{{latex(e_neg)}}}$ ",
            'end': end,
            }
        
        return walk
        
        pass


    def get_var_latex(self, v):
        if len(f"{v}") == 1:
            v_latex = f"{v}"
            return v_latex
        
        v_latex = f"\\left({latex(v)}\\right)"
        if isinstance(v, int):
            if v > 0:
                v_latex = f"{v}"
            pass

        return v_latex  
    

        
    pass
#class ArithmeticWalkHelper:
