'''
Created on 20-Jan-2022

@author: bthakur
'''

import inspect
import traceback
import random
import numpy as np

from sympy import simplify, Rational, latex, Symbol, srepr

from xv.km.util import latex_fraction
from sympy.core.expr import UnevaluatedExpr as ueval

from xv.km.ui import SvgHelper
from xv.util.data import CacheUtil
import matplotlib.pyplot as plt

from ...util import MathUtil
from xv.util import toIntOrFloat

class ArithmeticExpressionHelper:
    
    def __init__(self, **kwargs):
        self.verbose = kwargs.get('verbose', False)
        pass
    
    def get_solution(self, prob_expr):

        answer = simplify(prob_expr, rational = False)
        try:
            answer = toIntOrFloat(round(float(answer), 4)) 
        except:
            pass

        soln_items = []

        soln_item = f"""
        $\displaystyle {latex(prob_expr)}$
        """
        soln_items.append(soln_item.strip())
        
        soln_exprs, subs_dict = self._get_solution_steps(prob_expr)
        r_dict = self._convert_to_r_dict(subs_dict)
        
        for item_expr in reversed(soln_exprs):
            symbs = item_expr.free_symbols
            
            
            symbs_dict = {k: v_simp
                          for k,v,v_simp in subs_dict if k in symbs}

            r_dict_str = [f"Let $\displaystyle {latex(k)} = {latex(v)} = {latex(symbs_dict[k])}$" 
                                  for k,v in r_dict.items() if k in symbs]
            r_dict_str = ", ".join(r_dict_str)
            
            
            item_expr_subs = item_expr.subs(symbs_dict, evaluate = False)
            
            soln_item = f"""
            $\displaystyle = {latex(item_expr)} $ \t\t {r_dict_str}
            
            $\displaystyle = {latex(item_expr_subs)}$ 
            """
            soln_items.append(soln_item.strip())
            pass
        
        
        soln_item = f"""
        $\displaystyle = {latex(answer)}$ 
        """
        soln_items.append(soln_item.strip())
        soln_s = "<br/><br/>".join(soln_items)
        
        
        result = {
            'prob_expr': prob_expr,
            'soln_s': soln_s,
            'answer': answer,
            }
        return result

    def _convert_to_r_dict(self, subs_dict):
        r_dict = []
        
        subs_dict_rev = list(reversed(subs_dict))
        
        for idx, (k, v, v_simp) in enumerate(subs_dict_rev):
            sOut = rf"""
            $\displaystyle 
            {latex(k)} 
            =  {latex(v)} 
            = {latex(v_simp)}
            $
            """
            
            if idx == 0:
                r_dict.append([k, v])
                continue
            
            prev_k, prev_v, prev_v_simp = subs_dict_rev[idx - 1]
            
            cls = type(v)
            mod_args = [item for item in v.args]
            for mod_arg_idx, mod_arg in enumerate(v.args):
                if mod_arg == prev_v:
                    mod_args[mod_arg_idx] = prev_v_simp
                    pass
                pass
            mod_v = cls(*mod_args)
            r_dict.append([k, mod_v])
            pass
        
        r_dict = dict(r_dict)
        return r_dict
        pass

    
    def _get_solution_steps(self, prob_expr):
        subs_dict = []
        soln_exprs = []
        
        for idx in range(20):
            #call function
            if not subs_dict:
                input_key = None
                input_expr = prob_expr
            else:
                input_key, input_expr, input_expr_simp = subs_dict[-1]
                pass
            
            subs_dict_len = len(subs_dict)

            mod_expr = self._get_next_solution_step(input_expr, subs_dict)
            if (srepr(input_expr) == srepr(mod_expr) or 
                latex(input_expr) == latex(mod_expr) or 
                subs_dict_len == len(subs_dict)):
                break
            
            if not soln_exprs:
                soln_exprs.append(mod_expr)
            else:
                prev_soln_expr = soln_exprs[-1]
                
                mod_expr_subs = prev_soln_expr.subs(input_key, ueval(mod_expr))
                soln_exprs.append(mod_expr_subs)
            pass
        
        return soln_exprs, subs_dict
        pass

    def _get_next_solution_step(self, input_expr, subs_dict):
        '''
        simplifies input_expr and creates dict of variables R0, R1, R2, ... etc.
        '''
        cls = type(input_expr)
        mod_args = [item for item in input_expr.args]
        
        for idx, arg in enumerate(input_expr.args):
            if not hasattr(arg, 'args') or len(arg.args) <= 1:
                continue
            
            R_idx = Symbol(rf"R_{{{len(subs_dict)}}}")
            
            mod_arg_val = mod_args[idx]
            try:
                temp_val = float(simplify(mod_arg_val))
                mod_arg_val_simp = ueval(toIntOrFloat(round(temp_val, 4)) )
            except:
                mod_arg_val_simp = mod_arg_val
                pass
            
            subs_dict.append([R_idx, mod_arg_val, mod_arg_val_simp ])
            mod_args[idx] = R_idx
            pass
        
        mod_expr = cls(*mod_args)
        return mod_expr
        pass

    
    def _wrap_in_ol_list(self, rows):
        rows_str = [f"<li>{row}<br/><br/></li>"  
                    for row in rows]
        rows_str = "".join(rows_str)
        rows_str = f"<ol>{rows_str}</ol>"
        return rows_str
        pass

    pass
#class ArithmeticExpressionHelper:
