'''
Created on 12-Jul-2021

@author: bthakur

TR0 - simplify expression
TR1 - sec-csc to cos-sin
TR2 - tan-cot to sin-cos ratio
TR2i - sin-cos ratio to tan
TR3 - angle canonicalization
TR4 - functions at special angles
TR5 - powers of sin to powers of cos
TR6 - powers of cos to powers of sin
TR7 - reduce cos power (increase angle)
TR8 - expand products of sin-cos to sums
TR9 - contract sums of sin-cos to products
TR10 - separate sin-cos arguments
TR10i - collect sin-cos arguments
TR11 - reduce double angles
TR12 - separate tan arguments
TR12i - collect tan arguments
TR13 - expand product of tan-cot
TRmorrie - prod(cos(x*2**i), (i, 0, k - 1)) -> sin(2**k*x)/(2**k*sin(x))
TR14 - factored powers of sin or cos to cos or sin power
TR15 - negative powers of sin to cot power
TR16 - negative powers of cos to tan power
TR22 - tan-cot powers to negative powers of sec-csc functions
TR111 - negative sin-cos-tan powers to csc-sec-cot

use finite_difference

check https://github.com/sympy/sympy/issues/11155

>>> from sympy import *
>>> y = symbols('y')
>>> U = Function('U')(y)
>>> dU = Function('\delta U')(y)
>>> diff(U + dU,y).expand()
Derivative(U(y), y) + Derivative(\delta U(y), y)

https://stackoverflow.com/questions/46411124/how-to-rewrite-an-abstract-derivative-of-a-sum-as-a-sum-of-derivatives-in-sympy/46411350

'''

import inspect
import traceback
import random
from sympy import (latex, Symbol, symbols,
                   Rational,
                   )

from sympy import (cos, sin, tan, sec, csc, cot,
                   exp, I, pi,
                   sqrt,
                   simplify,
                   UnevaluatedExpr as ueval)
import sympy

from itertools import product

from sympy.simplify.fu import (TR111,TR22,TR16,TR15,TR14,
        TRmorrie,TR13,TR12i,TR12,TR11,TR10i,TR10,TR9,
        TR8,TR7,TR6,TR5,TR4,TR3,TR2i,TR2,TR1,TR0, 
        TRpower, fu)

# _func_tr_transform_helper        
# FuncTrTransformHelper        


from xv.util import UtilClass

class FuncTrTransformHelper(UtilClass):
    
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        pass
    
    def get_tr_transforms(self):
        from sympy.abc import x, y, A, B

        func_data = []
        
        func_data_item = [TR1, 
                     [csc, sec],
                     x
                     ]
        func_data.append(func_data_item)
        
        func_data_item = [TR2, 
                     [tan, cot],
                     x
                     ]
        func_data.append(func_data_item)
        
        #TR2i ignored as it is reciprocal of TR2
        
        func_data_item = [TR3, 
                     [sin, cos, tan, csc, sec, cot],
                     ueval(-x)
                     ]
        func_data.append(func_data_item)
        
        #TR4 ignored as it is gives values of trig functions 
        # with theta = 0 Pi/6 Pi/4 Pi/3 Pi/2
        
        
        
        tr_transforms = {}
        for tr_func, trig_funcs, input_expr in func_data:
            tr_transforms = self._get_tr_trig_func_transforms(tr_func, 
                                                    trig_funcs, 
                                                    input_expr, 
                                                    tr_transforms)
            pass
        
        
        expr_data = []
        
        expr_item = [TR5, 
                     sin(x)**2,
                     ]
        expr_data.append(expr_item)
        
        expr_item = [TR6, 
                     cos(x)**2,
                     ]
        expr_data.append(expr_item)
        
        expr_item = [TR7, 
                     cos(x)**2,
                     ]
        expr_data.append(expr_item)
        
        expr_item = [TR10, 
                     sin(x + y),
                     sin(x - y),
                     cos(x + y),
                     cos(x - y),
                     ]
        expr_data.append(expr_item)

        
        expr_item = [TR12, 
                     tan(x + y),
                     tan(x - y),
                     ]
        expr_data.append(expr_item)

        expr_item = [TR11, 
                     sin(2 * x),
                     cos(2 * x),
                     ]
        expr_data.append(expr_item)
        

        
        expr_item = [TR8, 
                     2 * sin(x) * cos(y),
                     2 * sin(x) * sin(y),
                     2 * cos(x) * cos(y),
                     ]
        expr_data.append(expr_item)
        
        
        expr_item = [TR9, 
                     sin(x) + sin(y),
                     sin(x) - sin(y),
                     cos(x) + cos(y),
                     cos(x) - cos(y),
                     ]
        expr_data.append(expr_item)
        
        expr_item = [TR22, 
                     tan(x) ** 2,
                     cot(x) ** 2,
                     ]
        expr_data.append(expr_item)
        
        expr_item = [TRpower, 
                     sin(x) ** 6,
                     cos(x) ** 3,
                     sin(x) ** 3 * cos(x) ** 4,
                     ]
        expr_data.append(expr_item)
        
        
        pi_by_9 = ueval(pi/9)
        pi_by_18 = ueval(pi/18)
        expr_item = [fu, 
                     Rational(1, 2) - cos(2*x)/2,
                     sin(x)/cos(x),
                     sin(50)**2 + cos(50)**2 + sin(pi/6),
                     sin(x)**4 - cos(y)**2 + sin(y)**2 + 2*cos(x)**2,
                     sin(x)*(cos(y) - sin(y)) + cos(x)*(sin(y) + cos(y)),
                     sqrt(3)*cos(x)/2 + sin(x)/2,
                     sqrt(3)*cos(x)/2 + sin(x)/2,
                     1-sin(2*x)**2/4-sin(y)**2-cos(x)**4,
                     cos(4*pi/9),
                     tan(7*pi/18)+tan(5*pi/18)-sqrt(3)*tan(5*pi/18)*tan(7*pi/18),
                     
                     cos(pi_by_9) * cos(2 *pi_by_9) * cos(3* pi_by_9) * cos(4 * pi_by_9),
                     tan(7 * pi_by_18) + tan(5 * pi_by_18) - sqrt(3) * tan(7 * pi_by_18) * tan(5 * pi_by_18),
                     
                     1 - Rational(1, 4) * sin(2 * x) ** 2 - sin(2 * y) ** 2  - cos(x) ** 4
              ]

        
        expr_data.append(expr_item)
        
        for expr_item in expr_data:
            tr_func = expr_item[0]
            exprs = expr_item[1:]
            tr_transforms = self._get_tr_expr_transforms(tr_func, 
                                                         exprs, 
                                                         tr_transforms)
            pass
        
        sOut = f"""
        tr_transforms
        {tr_transforms}
        """
        
        return tr_transforms
        pass

    def _get_tr_expr_transforms(self, tr_func, exprs, tr_transforms):
        for expr in exprs:
            expr_transform = self._get_tr_trig_func_transform(tr_func, expr)
            if not expr_transform:
                continue

            if not expr in tr_transforms.keys():
                tr_transforms[expr] = [expr_transform]
            else:
                if expr_transform in tr_transforms[expr]:
                    continue
                tr_transforms[expr].append(expr_transform)
            pass
        
        return tr_transforms
        pass
    
    
    def _get_tr_trig_func_transforms(self, tr_func, trig_funcs, input_expr, tr_transforms):
        for trig_func in trig_funcs:
            expr = trig_func(input_expr)
            expr_transform = self._get_tr_trig_func_transform(tr_func, expr)
            if not expr_transform:
                continue

            if not expr in tr_transforms.keys():
                tr_transforms[expr] = [expr_transform]
            else:
                if expr_transform in tr_transforms[expr]:
                    continue
                tr_transforms[expr].append(expr_transform)
            pass
        
        return tr_transforms
        pass
    
    def _get_tr_trig_func_transform(self, tr_func, expr):
        #expr = ueval(expr)
        mod_expr = simplify(expr)
        expr_transform = tr_func(mod_expr)
        if f"{expr}" == f"{expr_transform}":
            print(f"{tr_func.__name__}: {expr} is same as {expr_transform} [compared as str]")
            return None
        return expr_transform
        pass
    
    
    
    
    def _log_DELETED(self, sOut, to_print = None):
        if to_print is None:
            to_print = self.verbose
            
        if to_print:
            print(sOut)
    
            
    
    pass
#class FuncTrTransformHelper:
