'''
Search functionality using Trie data strictures
'''
partial_storage   = []  #global variable to store result for partial string search

class SearchEngine(object):
    #Class variable to store result for perfect whole string search
    full_match = []  
    
    def __init__(self):
        self.name_root = {}
        self.phone_root = {}
        
    def insert(self, info):
        '''
        This method is used for insertion of values        
        
        '''
        for index, data in enumerate(info):
            if index == 0: current_node, root = (self.name_root, 'name_root')
            if index == 1: current_node, root = (self.phone_root,'phone_root')

            data = str(data).lower() + '$'
            
            for char in data:
                try:
                    current_node = getattr(current_node[char], root)
                except KeyError:
                    current_node[char] = SearchEngine()
                    current_node = getattr(current_node[char], root)
            current_node[None] = info
                    
    def related_words(self,word,search_mode=None):
        '''
          Method used for finding words in case of partial string search
          
        '''
        if search_mode == 'name': node,atrr = (self.name_root, 'name_root')
        if search_mode == 'phone': node,atrr = (self.phone_root, 'phone_root')

        for char in node.keys():
            if getattr(node[char], atrr).has_key('$') :
                    self.full_match.append(getattr(getattr(node[char],atrr)['$'],atrr)[None])
            if char != None and char != '$':
                    node[char].related_words(word+char,search_mode)
        return self.full_match

    def check_suggestions(self,key,search_mode=None,combined_string=''):
        '''
        Combines given partial string characters and pass to related_words() as a single string
        
        '''
        global partial_storage
        if search_mode == 'name': current_node=self.name_root
        if search_mode == 'phone': current_node=self.phone_root
       
        key = str(key)

        try:
            #If there is no input from user, throw error message and exit program
            head_character = key[0] 
        except IndexError:
            print "Please provide atleast one charactert search string"
            return
        
        combined_string = combined_string + head_character
        
        if head_character in current_node:
            current_node = current_node[head_character]
            
            if len(key)>1:
                # Recursive call to combine all chars of key
                current_node.check_suggestions(key[1:],search_mode,combined_string) 
            else :
                partial_storage = current_node.related_words(combined_string,search_mode)

    def check_existance(self, word, search_mode=None):
        '''
        Method to check whole string is available in partial_storage dictionary.
    
        '''
        if search_mode == 'name':
            word = word.lower()+'$'
            current_node = self.name_root
            atrr = 'name_root'

        if search_mode == 'phone':
            word = str(word).lower()+'$'
            current_node = self.phone_root
            atrr = 'phone_root'

        
        total_length = len(word)
        
        for char in word:
            total_length = total_length - 1
            try:
                if total_length == 0:
                    return [True,current_node[word[-1]]]
                current_node = getattr(current_node[char],atrr)     
            except KeyError:
                return [False,0]
            
        if None in current_node:
            return [True,current_node[word[-1]]]
        else :
            return [False,0]

    def search(self,word):
        #If user input is integer, then search criteria is Phone
        #else search criteria is name
        try:
            word = int(word)
            search_mode = 'phone'
            atrr = 'phone_root'
        except ValueError:
            search_mode = 'name'
            atrr = 'name_root'
                
        word = str(word)
        #check to see if full string matches
        full_match= self.check_existance(word,search_mode) 
        
        if full_match[0]:
            full_match_word = getattr(full_match[1],atrr)[None]
            print "Name:"+full_match_word[0].strip('$') +'  '+ 'Phone:'+full_match_word[1].strip('$') +'  '+'Email:'+full_match_word[2]
        else:
            self.check_suggestions(word,search_mode)
            if partial_storage:
                for word in partial_storage:
                    print "Name:"+word[0].strip('$') +'  '+'Phone:'+word[1].strip('$') +'  '+'Email:'+word[2]
            else:
                print
                print "=>Sorry!! No match found..."

    def csv_to_xml(self):
        '''
        Save csv data as XML in the format:
        <root>
            <info>
                <name_field name="sunil">sunil</name_field>
                <phone_field phone="1234">1234</phone_field>
                <email_field email="sunil@yahoo.com">sunil@yahoo.com</email_field>
            </info>
            <info>...</info>
        </root>
        '''
        import xml.etree.cElementTree as ET

        with open(os.path.join(os.curdir, 'test.csv')
, 'rb') as f:
            reader = csv.reader(f)
            root = ET.Element("root")
            info = ET.SubElement(root, "info")

            for row in reader:
                name_field = ET.SubElement(info, "name_field")
                name_field.set("name", row[0])
                name_field.text = row[0]

                phone_field = ET.SubElement(info, "phone_field")
                phone_field.set("phone", row[1])
                phone_field.text = row[1]

                email_field = ET.SubElement(info, "email_field")
                email_field.set("email", row[2])
                email_field.text = row[2]

        tree = ET.ElementTree(root)
        tree.write(os.path.join(os.curdir, 'xml_test.xml'))
        return
    
if __name__ == "__main__":
    import csv
    import os
    
    t = SearchEngine()
    #CAUTION: config_data.csv file should be in the same path from where current module is running
    with open(os.path.join(os.curdir, 'config_data.csv')
, 'rb') as f:
        reader = csv.reader(f)
        for row in reader:
            t.insert([row[0],row[1],row[2]])

    search_value = raw_input("Enter words to search: ")
    t.search(search_value)
    
    print '\n'
    xml_choice = raw_input("Do you want to save file as XML document(y/n): ")
    if (xml_choice == 'y' or xml_choice == 'Y'):
        t.csv_to_xml()
        print
        print "Document saved to current working directory with the name xml_test.xml"

    
