#!/usr/bin/python
# -*- coding: UTF-8 -*-

"""
    This file is part of asd.
    
    asd is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    asd 0.1 Copyright 2004 Antonini Daniele <arpeda@gmail.com>
"""

import string, sys, re, stemmer, os.path

dictionary = {}

def usage():
    print "usage: search \t <word> [[and|or|not] <word>|<expr>] [+|- <espr>]"
    print "\t\t <expr> MUST begin with . (dot)"
    return

def remove_dups(lst):
    """ Removes duplicate elements from list. Drawbacks:
        - Returns an unsorted list. 
        - Does not work with lists, dicts etc. as list elements.
    """
    list = {}
    for item in lst:
        list[item] = None
    return list.keys()

def concatenate( lst1, lst2 ):
    return remove_dups( lst1 + lst2 )

def intersect( lst1, lst2 ):
    return filter( lambda x: x in lst1, lst2 )

def difference( lst1, lst2 ):
    return filter( lambda x: x not in lst2, lst1 ) #lst1 - lst2

def removeEmptyOperation( lst1, op ):
    lst2 = [op]
    return filter( lambda x: x not in lst2, lst1 )

""" Espande le espressioni regolari utilizzate nella ricerca"""
def expands( dict, pattern ):
    wordList = []

    p = re.compile( string.replace(pattern, ".", "", 1) ) #toglie il punto iniziale
    
    for i in dict.keys():
        try:
            p.match( i ).groups()
        except:
            continue

        wordList.append( i )

    return wordList


def filterOutput( outputList, expr ):
    filtered = []
    
    if re.match( '^\+', expr ):
        pattern = re.compile( string.replace(expr, "+", "", 1) )
        for item in outputList:
            if ( pattern.search(item) ):
                filtered.append( item )

    if re.match( '^-', expr ):
        pattern = re.compile( string.replace(expr, "-", "", 1) )
        for item in outputList:
            if not ( pattern.search(item) ):
                filtered.append( item )

    return filtered

"""Questa funzione inserisce dopo ogni elemento di list l'elemento op
"""
def createListWord( list, op ):

    index = range( 0, len(list) )
    index.reverse()
    for i in index:
        list.insert( i, op )

    return list

if not ( os.path.isfile("dictionary.asd") and  os.path.isfile("inverted-file.asd") and  os.path.isfile("man_page.asd") ):
    sys.exit( "Database not exists: execute cadb to create database" )


#Controllo dei parametri 
if len(sys.argv) == 1:
    usage(  )
    sys.exit( -1 )

"""
L'utente ha inserito almeno un termine da cercare per cui posso crearmi il dizionario per la ricerca dei valori
inseriti dall'utente
"""
fileDict = open( 'dictionary.asd','r' )

tmpList = fileDict.readlines()
#leggo riga per riga e creo l'hash
for value in tmpList:
    #splitto la stringa tramite lo spazio
    hashValue = string.split( value )
    #inserisco nel dizionario
    dictionary[hashValue[0]] = int( hashValue[1] )

fileDict.close(  )
    
"""
- ricerco termine nel dizionario e se presente ne prendo la lista degli indici delle man page
- elaboro la lista delle man page in questo modo:
  - per ogni termine in or concateno le liste
  - per ogni ! termine sottraggo la lista a quella di output
  - per ogni termine in and calcolo l'intersezione tra la lista corrente e quella di output
"""

#tmpWord =
wordToSearchTmp = [] #le parole inserite dall'utente espressioni regolari comprese
wordToSearch = [] #tutte le parole da cercare, le espressioni regolari sono state espanse
searchedWord = [] #le parole effettivamente cercate (le parole in input stemmate)
parameterForFilter = [] #le espressionin regolari per il filtraggio della lista
finded = 0

""" Lettura dei parametri in ingresso """
for i in range( 1, len(sys.argv) ):
    parameter = string.split( sys.argv[i] )

    if len( parameter ) > 1:
        wordToSearchTmp += parameter
    else:
        wordToSearchTmp.append( sys.argv[i] )


outputList = range( 1 , len(dictionary.keys()) ) #inizialmente considero tutte le man pages (per la gestione del caso "not word")

ps = stemmer.PorterStemmer()

""" """

"""controllo se ci sono espressioni regolari ed eventualmente le espando
i termini per il filtraggio dell'output vengono memorizzati in un array separato per successive elaborazioni"""
op = "";
for word in wordToSearchTmp:
    if re.match( 'or',word ):
        op = "or"
        continue

    if re.match( 'not', word ):
        op = "not"
        continue

    if re.match( 'and', word ):
        op = "and"
        continue

    if re.match( '^\+|^-', word ): #filtraggio output
        parameterForFilter.append( word )
        continue

    if re.match( '^\.', word ): #espressioni regolari
        expandedWords = expands( dictionary, word )
        expandedWords = createListWord( expandedWords, op )
        wordToSearch = wordToSearch + expandedWords
        continue

    if op != "":
        wordToSearch.append( op )

    word = (ps.stem( word, 0, len(word)-1 )).lower() #stemmo solo i termini inseriti dall'utente e non le eventuali espansioni delle espressioni regolari
    wordToSearch.append( word )
    op = ""


wordToSearch =  removeEmptyOperation( wordToSearch, "" )

""" Posso procedere con la ricerca delle man page che soddisfano la query dell'utente """

invertedFile = open( 'inverted-file.asd','r' )

operation = intersect #la prima operazione da compiere
for word in wordToSearch:
    """ se la parola  or o not o and devo cambiare il tipo di operazione da effettuare"""
    if re.match( 'or',word ):
        operation = concatenate
        searchedWord.append(word)
        continue

    if re.match( 'not', word ):
        operation = difference
        searchedWord.append(word)
        continue

    if re.match( 'and', word ):
        operation = intersect
        searchedWord.append(word)
        continue

    #print word + "  stata stemmata in:",
    #word = (ps.stem( word, 0, len(word)-1 )).lower()
    #print word

    searchedWord.append(word)

    if dictionary.has_key( word ):
        finded = 1
        beginList = dictionary[word]
        invertedFile.seek( beginList )
        tmpListLine = invertedFile.readline(  )
        tmpList = string.split( tmpListLine )
        tmpList = map( int, tmpList )
        outputList = operation( outputList, tmpList )
        operation = intersect #ripristino l'operazione di default
    else:
        outputList = []
        print "Il termine " + word + " non  stata trovato!"

invertedFile.close(  )


"""
A questo punto ho gli id delle man page che soddisfano la query dell'utente.
Traduco l'id della man pages nella sua corrispettiva descrizione
"""
print "Ho cercato", searchedWord

if not finded:
    sys.exit( " la ricerca non ha prodotto nessun risultato" )

manPage = {}

fileMan = open( 'man_page.asd','r' )
    
tmpList = fileMan.readlines()

#leggo riga per riga e creo l'hash
for value in tmpList:
    
    #splitto la stringa tramite lo spazio
    hashValue = value.split( ' ', 1 )

    #inserisco nel dizionario
    manPage[int(hashValue[0])] = hashValue[1]

fileMan.close(  )

print


""" Prima di stampare le man page filtro l'output se necessario """
outputFiltered = []

for idMan in outputList:
    outputFiltered.append( manPage[idMan] ); #prendo la descrizione delle man page per l'eventuale filtraggio

for pattern in parameterForFilter:
    outputFiltered = filterOutput( outputFiltered, pattern )


for element in outputFiltered:
    print element,

if not len(outputList):
    print "La ricerca non ha prodotto nessun risultato"

print
