from orangescape.model.engine.exception.Error import FormulaArgumentError
from orangescape.model.engine.exception.ErrorMessage import *
from orangescape.model.engine.execution.exFunctions import getNumber, getInteger
from orangescape.model.server.util.ConversionUtil import convertDateTime,getFormattedNumberValue
import re

def CHAR(argList):
    number = getInteger(argList[0])
    if number < 1:
        raise FormulaArgumentError,Engine2007 % ('CHAR','lesser',1)    #  : Excel Error Code
    elif number > 255 :
        raise FormulaArgumentError,Engine2007 % ('CHAR','greater',255)
    else:
        return chr(number)

def CONCATENATE(argList):
    return "".join(argList)

def FIND(argList):
    # It's for searching a string/character in the given string.
    # Find has 3 arguments but 3rd is optional so we used two "if" statements here.
    # It is case sensitive
    find_text=argList[0]
    within_text=argList[1]
    if len(argList)==3:
        start_num=getInteger(argList[2])-1
        if start_num < 0:
            raise FormulaArgumentError,Engine2017 % (3,'FIND',argList[2])
        result = within_text.find(find_text,start_num)
    elif len(argList)==2:
        result = within_text.find(find_text)
    if result<0:
#        raise FormulaArgumentError,Engine2015 % ('FIND',find_text,within_text,0)
        return -1
    else:
        return result+1

def LEFT(argList):
    #This function will give a string from the beginning to a specified number (i.e) argList[1].
    #Reason for If statement-If users didn't give the 2nd argument by default it will give the first letter.
    text = argList[0]
    if len(argList) == 2:
        num_chars=getInteger(argList[1])
        if num_chars < 0:
            raise FormulaArgumentError,Engine2007 % ('LEFT','lesser',0)
        return text[0:num_chars]
    elif len(argList)==1:
        return text[0:1]

def LEN(argList):
    #It will return the length of the string"
    return float(len(argList[0]))

def LOWER(argList):
    return argList[0].lower()

def MID(argList):
    #To get a part of text within the given text by giving starting position and length
    text=argList[0]
    start_num=getInteger(argList[1])-1
    num_chars=getInteger(argList[2])
    if (start_num < 0 or num_chars < 0) or ((argList[1]-1)<0 or argList[2]<0):
        raise FormulaArgumentError,Engine2007 % ("MID","lesser" ,1)
    if start_num>len(text):
        return ""
    elif (num_chars+start_num)>len(text):
        return text[start_num:]
    else:
        return text[start_num:start_num+num_chars]

def REPLACE(argList):
    old_text=argList[0]
    start_num=getInteger(argList[1])-1
    nof_chars = getInteger(argList[2])
    new_text=argList[3]
    if start_num<0 or nof_chars<0:
        raise FormulaArgumentError,Engine2007 % ("REPLACE","lesser" ,1)
    else:
        if start_num>=len(old_text):
            start_num = len(old_text)
        first_string=old_text[:start_num]
        last_string=old_text[start_num+nof_chars:]
        return first_string+new_text+last_string

def RIGHT(argList):
    #This function will return the specified number of text from the end of the string
    #By default it will send last letter of the string (user will give only one argument).To find this, we used "IF" statement
    text = argList[0]
    if len(argList)>=2:
        num_chars = getInteger(argList[1])
        if argList[1] < 0:
            raise FormulaArgumentError,Engine2007 % ('RIGHT','lesser',0)
        strLen=len(text)
        if num_chars > strLen:
            startPos = 0
        else:
            startPos=strLen-num_chars
        return text[startPos:strLen]
    else:
        strLen=len(text)
        startPos=strLen-1
        return text[startPos:strLen]

def SEARCH(argList):
    # It's for searching a string/character in the given string.
    # Search has 3 arguments but 3rd is optional so we used two "if" statements here.
    # It is not case sensitive
    find_text=argList[0].lower()
    within_text=argList[1].lower()
    start_num = 0
    if len(argList)==3:
        start_num=getInteger(argList[2])-1
        if start_num < 0:
            raise FormulaArgumentError,Engine2017 % (3,'SEARCH',argList[2])

    if re.search(r'[*?]',find_text):
        find_text = find_text.replace('*', '.*')
        find_text = find_text.replace('?', '.')
        result = within_text.find(re.findall(find_text,within_text)[0],start_num)
    else:
        result = within_text.find(find_text,start_num)

    if result<0:
        raise FormulaArgumentError,Engine2015 % ('SEARCH',find_text,within_text,start_num)

    return result+1

def TRIM(argList):
    return " ".join(argList[0].strip().split())

def SUBSTITUTE(argList):
    # It sill substitute the new text(argList[2]) instead of the old text (argList[1]) within the specified text (argList[0])
    # fourth argument is optional. It specifies in which occurrence to substitute.
    # It is case sensitive.
    text=argList[0]
    old_text=argList[1]
    new_text=argList[2]
    result = ""
    if (len(argList)-3)%2==0 and (len(argList) != 3):
        replacedText = SUBSTITUTE(argList[0:3])
        newList = argList[3:len(argList)]
        newList.insert(0,replacedText)
        return SUBSTITUTE(newList)
    if len(argList)==4:
        instance_num = getInteger(argList[3])
        instance_list = re.findall(old_text, text)
        if len(instance_list) >= instance_num:
            split = re.split(old_text, text)
            for index in xrange(len(split)):
                if (index == instance_num-1 ):
                    result += split[index] + new_text
                else:
                    if index <= len(instance_list)-1:
                        result += split[index]+instance_list[index]
                    else:
                        result += split[index]
            return result
        elif instance_num <= 0:
            raise FormulaArgumentError,Engine2007 % ('SUBSTITUTE' , 'lesser', 1)
        else:
            return text
    elif len(argList)==3:
        return re.sub(old_text, new_text, text)

def UPPER(argList):
    return argList[0].upper()

def VALUE(argList):
    # Converts a text string that represents a number to a float value
    try:
        return getNumber(argList[0])
    except ValueError, e:
        raise FormulaArgumentError,Engine1004 % (1,'Number',argList[0],'VALUE')

def T(argList):
    if type(argList[0]) in (str,unicode):
        return argList[0]
    else:
        return ""

def EXACT(argList):
    if argList[0] == argList[1]:
        return 1.0
    else:
        return 0.0

def PROPER(argList):
    return argList[0].title()

def REPT(argList):
    result = ''
    str = ''
    str = argList[0]
    if argList[1] < 0:
        raise FormulaArgumentError,Engine2017 % (2,'REPT',argList[1])
    for arg in range(getInteger(argList[1])):
        result = result.__add__(str)
    return result

def CODE(argList):
    # Get the ASCII number of  first letter of the given string
    text = str(argList[0] or "")
    if len(text) == 0:
        raise FormulaArgumentError,Engine2017 % (1,'CODE',"")
    return ord(text[0])

def CLEAN(argList):
    l = list()
    for arg in argList[0]:
        if ord(arg) not in (127, 129, 141, 143, 144, 157) and ord(arg) > 31:
            l.append(arg)
    return "".join(l)

def TEXT(argList):
    value = argList[0]
    format = argList[1]

    if format.__contains__(","):
        raise FormulaArgumentError,Engine2027
    elif format.__contains__("0") or format.__contains__("#") or format.__contains__(".") or format.__contains__("$"):
        return getFormattedNumberValue(value, format)
    else:
        return convertDateTime(value, format)

def FORMAT(argList):
    def replace(matchObj):
        try:
            if matchObj.group(0) == '}}':return '}'
            elif matchObj.group(0) == '{{':return '{'
            else:return args[int(matchObj.group(0)[1:-1])]
        except IndexError, e:
            raise FormulaArgumentError,Engine2028 % (matchObj.group(0))

    formatString = argList[0]
    args = argList[1:]

    rxPattern = re.compile("({{2})|(}{2})|({{1}\d}{1})")
    formatString = re.sub(rxPattern, replace, formatString)
    return formatString
