from exception import CalculatorException
from complex import Complex
import math
import re

def parse(fx):
    return eval(fx, 0)
    

# fx(xi) or f(x,y,z,...) where xi is a var dictionary 
def eval(fx, xi):
    fx = fx.strip().lower().replace(" ","")


    value = 0.0
    number = ""
    function = ""
    hasNumber = False
    hasFunction = False
    isImaginary = False
    
    i = 0
    
    while (i < len(fx)):
        character = fx[i]
        
        if character == '*':     
            if hasNumber == True and isImaginary == False:
                numb = number
                new_fx = nextFunction(fx[i + 1:len(fx)])
                value = Complex.mul(Complex(float(numb),0), eval(new_fx, xi))
                i = i + len(new_fx)
                hasNumber = False
                number = ""
            
            elif hasNumber == True and isImaginary == True:
                numb = number
                new_fx = nextFunction(fx[i + 1:len(fx)])
                value = Complex.mul(Complex(0, float(numb)),eval(new_fx, xi))
                i = i + len(new_fx)
                hasNumber = False
                number = ""
            elif hasFunction == True:
                new_fx = nextFunction(fx[i + 1:len(fx)])
                value = Complex.mul(eval(function, xi), eval(new_fx, xi))
                i = i + len(new_fx)
                hasFunction = False
                function = ""
            else:
                new_fx = nextFunction(fx[i + 1:len(fx)])
                value = Complex.mul(value, eval(new_fx, xi))
                i = i + len(new_fx)
         
         
        elif character == '+':   
    
            if hasNumber == True and isImaginary == False:
                numb = number
                new_fx = nextMinusFunction(fx[i + 1:len(fx)])
                value = Complex.add(Complex(float(numb), 0),eval(new_fx, xi))
                i = i + len(new_fx)
                hasNumber = False
                number = ""
            
            elif hasNumber == True and isImaginary == True:
                numb = number
                new_fx = nextMinusFunction(fx[i + 1:len(fx)])
                value = Complex.add(Complex(0, float(numb)), eval(new_fx, xi))
                i = i + len(new_fx)
                hasNumber = False
                number = ""    
            elif hasFunction == True:
                new_fx = nextMinusFunction(fx[i + 1:len(fx)])
                value = Complex.add(eval(function, xi), eval(new_fx, xi))               
                i = i + len(new_fx)
                hasFunction = False
                function = ""
            else:
                new_fx = nextMinusFunction(fx[i + 1:len(fx)])
                value = Complex.add(value,eval(new_fx, xi))
                i = i + len(new_fx)
                
                   
        elif character == '-':   
    
            if hasNumber == True and isImaginary == False:
                numb = number
                new_fx = nextMinusFunction(fx[i + 1:len(fx)])
                value = Complex.sub(Complex(float(numb), 0), eval(new_fx, xi))
                i = i + len(new_fx)
                hasNumber = False
                number = ""
            
            elif hasNumber == True and isImaginary == True:
                numb = number
                new_fx = nextMinusFunction(fx[i + 1:len(fx)])
                value = Complex.sub(Complex(0, float(numb)), eval(new_fx, xi))
                i = i + len(new_fx)
                hasNumber = False
                number = ""
                
            elif hasFunction == True:
                new_fx = nextMinusFunction(fx[i + 1:len(fx)])
                value = Complex.sub(eval(function, xi), eval(new_fx, xi))
                i = i + len(new_fx)
                hasFunction = False
                function = ""
            else:
                new_fx = nextMinusFunction(fx[i + 1:len(fx)])
                value = Complex.sub(value, eval(new_fx, xi))

                i = i + len(new_fx)
                
        elif character == '/':     
            
            if hasNumber == True and isImaginary == False:
                numb = number
                new_fx = nextFunction(fx[i + 1:len(fx)])
                value = Complex.div(Complex(float(numb), 0), eval(new_fx, xi))
                i = i + len(new_fx)
                hasNumber = False
                number = ""
                
            elif hasNumber == True and isImaginary == True:
                numb = number
                new_fx = nextFunction(fx[i + 1:len(fx)])
                value = Complex.div(Complex(0, float(numb)), eval(new_fx, xi))
                i = i + len(new_fx)
                hasNumber = False
                number = ""
            
            elif hasFunction == True:
                new_fx = nextFunction(fx[i + 1:len(fx)])
                value = Complex.div(eval(function, xi), eval(new_fx, xi))
                i = i + len(new_fx)
                hasFunction = False
                function = ""
            else:
                new_fx = nextFunction(fx[i + 1:len(fx)])
                value = Complex.div(value, eval(new_fx, xi))

                i = i + len(new_fx)
    
        elif character == '^':     
            
            if hasNumber == True and isImaginary == False:
                numb = number
                new_fx = nextFunction(fx[i + 1:len(fx)])
                value = Complex.rpow(eval(new_fx, xi), float(numb))
                i = i + len(new_fx)
                hasNumber = False
                number = ""
                
            elif hasNumber == True and isImaginary == False:
                numb = number
                new_fx = nextFunction(fx[i + 1:len(fx)])
                value = Complex.pow(eval(new_fx, xi), Complex(0, float(numb)))
                i = i + len(new_fx)
                hasNumber = False
                number = ""
                
            elif hasFunction == True:
                new_fx = nextFunction(fx[i + 1:len(fx)])
                value = Complex.pow(eval(function, xi), eval(new_fx, xi))
                i = i + len(new_fx)
                hasFunction = False
                function = ""
            else:
                new_fx = nextFunction(fx[i + 1:len(fx)])
                value = Complex.pow(value, eval(new_fx, xi))     
                i = i + len(new_fx)
                
        elif character == '0' or character == '1' or character == '2' or character == '3' or character == '4' or character == '5' or character == '6' or character == '7' or character == '8' or character == '9':
            
            if hasFunction == True:
               function = function + character
               
            else:    
                hasNumber = True
                number = number + character
                if i == len(fx) - 1:
                    value = Complex(float(number), 0)
                    number = ""
                    hasNumber = False
                
        elif character == '.':
            if i == len(fx) - 1:
                raise CalculatorException("The function is not well-formed")
            
            if hasNumber == True and len(number) > 0:
                number = number + character
                 
        elif character == '(':
    
            if i == len(fx) - 1:
                raise CalculatorException("The function is not well-formed")
            
    
            new_fx = fx[i + 1:nextBracket(fx)]
            if hasFunction==True:
                if function == "sin":
                    value = eval(new_fx, xi).__sin__()                        
    
                elif function == "cos":
                    value = eval(new_fx, xi).__cos__()
                    
                elif function == "tan":
                    value = eval(new_fx, xi).__tan__()
                
                elif function == "sinh":
                    value = eval(new_fx, xi).__sinh__()
                
                elif function == "cosh":
                    value = eval(new_fx, xi).__cosh__()
    
                elif function == "tanh":
                    value = eval(new_fx, xi).__tanh__()
    
                elif function == "asin":
                    value = eval(new_fx, xi).__asin__()
                    
                elif function == "acos":
                    value = eval(new_fx, xi).__acos__()  
                                          
                elif function == "atan":
                    value = eval(new_fx, xi).__atan__()
                
                #log10    
                elif function == "log10":
                    value = eval(new_fx, xi).__log10__()
                
                #log
                elif function == "log":
                    value = eval(new_fx, xi).__log__()
                    
                elif function == "sqrt":                    
                    value = eval(new_fx, xi).__sqrt__()
                    
                #elif function == "cbrt":
                #    value = math.cbrt(float(eval(new_fx, xi)))
                    
                else:
                    raise CalculatorException("The function is not well-formed")
                
    
                hasFunction = False
                function = ""
    
            else:
                value = eval(new_fx, xi)  
                              
            i = i + len(new_fx) + 1
    
            
        elif character == ')':     
            raise CalculatorException(" '(' is not finished ")
    
        elif character == ' ':  
            j = 0
            
        elif character == 'i' or character == 'j':
             
            if hasFunction == False:
 
                if hasNumber == True:

                    value = Complex(0, float(number))
                    number = ""
                    isImaginary = True
                else:
                    value = Complex(0, 1)
                    isImaginary = True
                    
                    
            else:

                function = function + character
                hasFunction = True

                if i == len(fx) - 1:
                    
                    if function == "e":
                        value = Complex(math.e,0)
    
                    elif function == "pi":
                        value = Complex(math.pi,0) 
                    
                    else:
                        possibleValue = getValue(function, xi)
                        
                        if isinstance(possibleValue, str):
                            value = eval(possibleValue, xi)
                        else:
                            value = possibleValue

                   
        else:
            if isValidCharacter(character):
                function = function + character
                hasFunction = True
    
                if i == len(fx) - 1:
    
                    if function == "e":
                        value = Complex(math.e,0)
    
                    elif function == "pi":
                        value = Complex(math.pi,0) 
                    
                    else:
                        possibleValue = getValue(function, xi)
                        
                        if isinstance(possibleValue, str):
                            value = eval(possibleValue, xi)
                        else:
                            value = possibleValue                       
                                              
            else:
                raise CalculatorException("Invalid character")
           
    
        i += 1
    return value

def getValue(function, xi):
    
    value = 0
    if len(function) == 1:
                            
        if isinstance(xi, dict):
            
            if function.lower() in xi:
                value = xi[function.lower()]
            elif function.upper() in xi:
                value = xi[function.upper()]
            else:
                raise CalculatorException("function is not well defined")          
            
        elif isinstance(xi, int):
            value = Complex(xi,0)
        elif isinstance(xi, float):
            value = Complex(xi,0)
        elif isinstance(xi, Complex):
            value = xi
        elif isinstance(xi, complex):
            value = Complex(xi.real,xi.imag)
        elif isinstance(xi, str):
            value = eval(xi, xi)
        else:
            raise CalculatorException("function is not well defined")
        
    return value
        
    
def nextFunction( fx):
    
    fx = fx.strip()
    
    result = ""
    
    i = 0
    
    while (i < len(fx)):
        character = fx[i]
        
        if character == '+' or character == '-' or character == '*' or character == '/':
            i = len(fx)
            
        elif character == '^' or character == '.' or character == ' ':
            result += character
        
        elif character == '(':
                   
            new_fx = fx[i:nextBracket(fx)+1]
            result += new_fx
            i = (i +len(fx))-1

        elif character == ')':
            raise CalculatorException(" '(' is not finished ")
        
        else:
            if isValidNumericAndCharacter(character):
                result = result + character
            else:
                raise CalculatorException("Invalid character")
        i+=1
            
    return result

def nextMinusFunction(fx):
    
    fx = fx.strip()
    
    result = ""
        
    i = 0
    
    while (i < len(fx)):
   
        character = fx[i]
        
        if character == '+' or character == '-' :
            i = len(fx)
            
        elif character == '^' or character == '.' or character == ' ' or character == '*' or character == '/':
            result += character
        
        elif character == '(':
                   
            new_fx = fx[i:nextBracket(fx)+1]
            result += new_fx
            i = (i +len(fx))-1

        elif character == ')':
            raise CalculatorException(" '(' is not finished ")
        
        else:
            if isValidNumericAndCharacter(character):
                result = result + character
            else:
                raise CalculatorException("Invalid character")
        
        i+=1    
    return result



def isValidCharacter(character):
    
    regexCharacter = re.compile('[a-z]')
    result = False
    
    if regexCharacter.match(character):
        result = True
    else: 
        result = False
    
    return result

def isValidNumericAndCharacter(character):
    
    regexNumericAndCharacter = re.compile('\w')
    result = False
    
    if regexNumericAndCharacter.match(character):
        result = True
    else: 
        result = False
    
    return result


def nextBracket(fx):

    result = 0
    count = 0
 
    
    for i in range(0,len(fx)):
        character = fx[i]
        
        if character == '(':
            result = i
            count = count+1
            
        elif character == ')':   
            result = i
            count=count-1
            if count == 0:
                return i
        else:
            result=i        
        
    if count !=0:
        raise CalculatorException ("Exception: ( is not finished")
    
    return result
