#!/usr/bin/python

###lojval was created by Zachary Spector, aka lojbaj
###
###I hereby release this code into the public domain.  In case this is
###not possible, I authorize anyone to use this code for any purpose.
###
###However, I humbly request that you keep my name on it.

###  begin configuration  ###

#change these to the proper locations of your vocabulary files
cmavo = "/usr/share/lojban/cmavo.txt"
gismu = "/usr/share/lojban/gismu.txt"
rafsi = "/usr/share/lojban/rafsi.txt"
lujvo = "/usr/share/lojban/NORALUJV.txt"
#and change this to the location of your flashcard deck
flashcarddeck = "lojban.csv"
#and this, to the format of your flashcard deck; currently, acceptable
#values are "pauker" and "csv"
flashtype = "csv"

#change this to True if you'd prefer for every successful, unique
#match to be added to your flashcard deck
flash = False

#change this to True if you want to search through comparisons and notes
verbose = False

#By default, I'll put the English gloss on the front of every
#flashcard, and the definition on the back. If you'd like it the other
#way around, change this to True.
lojban_on_front = False

#you can customize the output style by modifying these functions
def printGismu(desired_gismu, english_gloss, rafsi_for_this_gismu, definition, annotation):
    print desired_gismu + "\t" + rafsi_for_this_gismu + "\t" + english_gloss + "\n\t" + definition
    if verbose:
        print "\tNotes: " + annotation

def printRafsi(desired_rafsi, associated_gismu, english_gloss):
    print desired_rafsi + "\t" + associated_gismu + "\t" + english_gloss

def printLujvo(desired_lujvo, english_gloss, lujvo_roots):
    print desired_lujvo + "\t" + english_gloss + "\n\t" + lujvo_roots

def printCmavo(desired_cmavo, english_gloss, definition, selmaho, annotation):
    print desired_cmavo + "\t" + selmaho + "\t" + english_gloss + "\n\t" + definition
    if verbose:
        print "\tNotes: " + annotation

#Note that lojval makes certain assumptions about how the vocabulary
#files are formatted. If you have different versions of said files,
#strange behavior may result. This version of lojval is known to work
#with:
#-LogFlash cmavo list 26 August 2002
#-gismu list 9/26/94
#-the list of rafsi from the package "lojban-common" version 1.4-1 from the
#Ubuntu Gutsy Universe repository
#-the version of NORALUJV.txt from the same package

###  end configuration  ###

#TODO:
#-support for more flashcard formats
#-hopefully a GUI
#-a "lujvo dissection" function, to pull apart the various rafsi in a
#lujvo and display their corresponding gismu; requires morphological
#parsing
#-"preferential" scoping: search the gismu first, if there's a perfect
#match, stop there; otherwise continue to the lujvo


import sys, os, re, gzip, copy, csv

testing = False

### begin class definitions ###

class Notifier:
    finalResult = ""
    specifydeck = False

    def noFlashcardFile(self, filename=None):
        if self.specifydeck:
            filename = flashcarddeck
        if filename:
            print "\nThere's no flashcard file at %s. I'll create one for you. " % filename
        else:
            print "\nThe flashcard file is missing. I'll create one for you. "

    def addWordToFlashcards(self, word, filename=None):
        if self.specifydeck:
            filename = flashcarddeck
        if filename:
            print "\nNow adding \"%s\" to the flashcard file at %s." % (word, filename)
        else:
            print "\nNow adding \"%s\" to your flashcard deck. " % word

    def wordAlreadyExists(self, word, filename=None):
        if self.specifydeck:
            filename = flashcarddeck
        if filename:
            print '\nThe word "%s" is already in the flashcard deck at %s. I won\'t add another copy.' % (word, filename)
        else:
            print '\nThe word "%s" is already in your flashcard deck. I won\'t add another copy.' % word

    def noPerfectResult(self, word, filename=None):
        if self.specifydeck:
            filename = flashcarddeck
        if filename:
            print '\nSince there was no exact match for "%s", I won\'t add it to the flashcard file at %s.' % (word, filename)
        else:
            print '\nSince there was no exact match for "%s", I won\'t add it to your flashcards.' % word

    def IOError(self, filename):
        print "\nI couldn't access the flashcard file at %s. Make sure there's a folder there and you have write permissions for it. " % filename

    def notpauker(self, filename):
        print '\n' + filename + " doesn't look like a Pauker file, so I won't add anything to it. If you're sure it's a Pauker file, put .pau or .pau.gz on the end. "

    def notcsv(self, filename):
        print '\n' + filename + " doesn't look like a CSV file, so I won't add anything to it. If you're sure that it is, put .csv on the end."

notify = Notifier()

class FlashcardAdder:
    def pauker(self, flashcarddeck, front, back):
        if flashcarddeck[-5:] != "pau.gz":
            if flashcarddeck[-3:] != "pau":
                notify.notpauker(flashcarddeck)
                return

        if testing:
            print "Using the Pauker flashcard deck at " + flashcarddeck
        try:
            lojbancards = gzip.open(flashcarddeck).read()
            used_gzip = True
        except (IOError):
            try:
                lojbancards = open(flashcarddeck).read()
                used_gzip = False
            except:
                notify.noFlashcardFile()
                try:
                    gzip.open(flashcarddeck, "wb").write("""<!--
        This is a lesson file for Pauker (http://pauker.sourceforge.net)
        -->
        <Lesson LessonFormat="1.7">
        <Description/>
        <Batch>
        </Batch>
        </Lesson>""")
                    lojbancards = gzip.open(flashcarddeck, "r").read()
                    used_gzip = True
                except IOError:
                    notify.IOError(flashcarddeck)
                    return


        if back in lojbancards:
            notify.wordAlreadyExists(notify.finalResult)
            return

        addcard = """\n<Card>
    <FrontSide Orientation="LTR" RepeatByTyping="false">
    <Text>%s</Text>
    <Font Background="-1" Bold="false" Family="Dialog" Foreground="-16777216" Italic="false" Size="12"/>
    </FrontSide>
    <ReverseSide Orientation="LTR" RepeatByTyping="false">
    <Text>%s</Text>
    <Font Background="-1" Bold="false" Family="Dialog" Foreground="-16777216" Italic="false" Size="12"/>
    </ReverseSide>
    </Card>""" % (front, back)

        cardstart = re.search("<Batch>", lojbancards)
        if cardstart is None:
            print "The flashcard file is malformed. I can't work with it."
            return
        cardstart = cardstart.end()

        lojbancards = lojbancards[:cardstart] + addcard + lojbancards[cardstart:]
        if testing:
            print "Removing " + flashcarddeck
        os.remove(flashcarddeck)

        if used_gzip:
            gzip.open(flashcarddeck, "wb").write(lojbancards)
        else:
            open(flashcarddeck, "wb").write(lojbancards)

    def csv(self, csvfile, front, back):
        if csvfile[-3:] != 'csv':
            notify.notcsv(csvfile)
            return

        try:
            deckcontents = open(csvfile, "r").read()
            if back in deckcontents:
                notify.wordAlreadyExists(notify.finalResult)
                return
        except:
            notify.noFlashcardFile()
        try:
            csvwriter = csv.writer(open(csvfile, "w"))
        except IOError:
            notify.IOError()
            return
        csvwriter.writerow((front, back))

add = FlashcardAdder()

### end class definitions ###
### begin parsing command line arguments ###

i = 1
scope = []
searchterms = []
while i < len(sys.argv):
    if sys.argv[i] in ('-cmavo', '-c') and not (cmavo in scope):
        scope.append(cmavo)
    elif sys.argv[i] in ('-gismu', '-g') and not (gismu in scope):
        scope.append(gismu)
    elif sys.argv[i] in ('-rafsi', '-r') and not (rafsi in scope):
        scope.append(rafsi)
    elif sys.argv[i] in ('-lujvo', '-l') and not (lujvo in scope):
        scope.append(lujvo)
    elif sys.argv[i] in ('-pauker', '--pauker', '-p'):
        flash = True
        flashtype = "pauker"
    elif sys.argv[i] in ('-csv', '--csv'):
        flash = True
        flashtype = "csv"
    elif sys.argv[i] in('-f', '-flash', '-flashcard', '--flash', '--flashcard'):
        flash = True
    elif sys.argv[i][:3] == '-f=':
        flash = True
        notify.specifydeck = True
        flashcarddeck = sys.argv[i][3:]
    elif sys.argv[i] in ('-all', '--all', '-a', '-rovalsi', '--rovalsi', '-ro', '--ro'):
        scope = [cmavo, rafsi, gismu, lujvo]
    elif sys.argv[i] in ('-v', 'verbose'):
        verbose = True
    elif sys.argv[i] == '-test':
        testing = True
    else:
        searchterms.append(sys.argv[i])

    i += 1

if scope == []:
    scope = [gismu]

### end parsing command line arguments ###
### begin parsing/printing functions ###
    

def parseResults(old_searchresults, searchterm, vocabfile):
    searchresults = copy.deepcopy(old_searchresults)
    results = []
    for readline in searchresults:
        
        if "9/26/94" in readline:
#Sometimes a search will turn up the first line of the vocabfile, which is
#just version information and doesn't contain any vocabulary. Discard
#this result.
            continue

        if cmavo == vocabfile:
            keyspots = (readline[0:11], readline[11:20], readline[20:62])

        elif gismu == vocabfile:
            keyspots = (readline[1:7], readline[7:10], readline[11:14], readline[15:19], readline[20:41], readline[41:62])

        elif rafsi == vocabfile:
            keyspots = (readline[0:5], readline[5:10], readline[12:])

        elif lujvo == vocabfile:
            lujvo_roots = readline[31:]
            lujvo_root_end = lujvo_roots.find("x1")
            lujvo_roots = lujvo_roots[:lujvo_root_end]
            lujvo_roots_list = []
            this_lujvo_root = ""
            for char in lujvo_roots:
                if char.isalpha():
                    this_lujvo_root += char
                else:
                    lujvo_roots_list.append(this_lujvo_root)
                    this_lujvo_root = ""

            gloss_start = readline.find(":")
            gloss_end = readline.rfind(":")
            gloss = readline[gloss_start:gloss_end].strip(":")
            keyspots = [readline[:31]]
            for item in lujvo_roots_list:
                keyspots.append(item)
            keyspots.append(gloss)
            keyspots = tuple(keyspots)
        else:
            print "Error: Don't know how to deal with the vocabulary file."
            exit()
        results.append(keyspots)
    
    results.sort()
    return results


def printOneResult(result, vocabfile, doflash=False):
    if gismu == vocabfile:
        desired_gismu = result[1:6]
        english_gloss = result[20:41].strip()
        rafsi_for_this_gismu = result[7:19]
        definition = result[62:159].strip()
        annotation = result[159:].strip()
        printGismu(desired_gismu, english_gloss, rafsi_for_this_gismu, definition, annotation)
        if lojban_on_front:
            front = desired_gismu
            back = "\t".join((english_gloss, rafsi_for_this_gismu, definition))
        else:
            front = english_gloss
            back = "\t".join((desired_gismu, rafsi_for_this_gismu, definition))
    if rafsi == vocabfile:
        desired_rafsi = result[0:4]
        associated_gismu = result[5:10]
        english_gloss = ""
        printRafsi(desired_rafsi, associated_gismu, english_gloss)
        if lojban_on_front:
            front = desired_rafsi
            back = ""
        else:
            front = english_gloss
            back = ""
    if lujvo == vocabfile:
        parsed = parseResults([result], None, vocabfile)
        desired_lujvo = parsed[0][0].strip()
        english_gloss = parsed[0][-1].strip()
        lujvo_roots = result[result.rfind(":"):].strip(":").strip()
        printLujvo(desired_lujvo, english_gloss, lujvo_roots)
        if lojban_on_front:
            front = desired_lujvo
            back = english_gloss
        else:
            front = english_gloss
            back = desired_lujvo
    if cmavo == vocabfile:
        desired_cmavo = result[0:11].strip()
        english_gloss = result[20:62].strip()
        definition = result[62:168].strip()
        selmaho = result[11:20].strip()
        annotation = result[168:].strip()
        printCmavo(desired_cmavo, english_gloss, definition, selmaho, annotation)
        if lojban_on_front:
            front = desired_cmavo
            back = "\t".join((english_gloss, selmaho, definition))
        else:
            front = english_gloss
            back = "\t".join((desired_cmavo, selmaho, definition))
    if testing:
        print "English gloss: " + english_gloss
    if doflash:
        notify.addWordToFlashcards(notify.finalResult)
        if flashtype == "pauker":
            add.pauker(flashcarddeck, front, back)
        elif flashtype == "csv":
            add.csv(flashcarddeck, front, back)
        else:
            print "You specified a flashcard format that I don't understand. "

                        
def printResults(results, vocabfile):
    if (results == []) or (results == None):
        print "No results."
        return
    for item in results:
        printOneResult(item, vocabfile)
            

### end parsing/printing functions ###
### begin actually handling the search terms ###
            
for term in searchterms:
    
    if len(searchterms) > 1:
        print '\nResults for "%s":' % term

    for wordclass in scope:
        wordvocabfile = open(wordclass, "r")
        readline = wordvocabfile.readline()
        wordclassresults = []
        
        while readline != "":
            if term == "*":
                linematch = True
            else:
                linematch = re.search(term, readline)
            if linematch:
                wordclassresults.append(readline)
            readline = wordvocabfile.readline()
                    
        wordvocabfile.close()
        wordclassresults.sort()
        if wordclass is cmavo:
            print "===cmavo==="
        elif wordclass is gismu:
            print "===gismu==="
        elif wordclass is rafsi:
            print "===rafsi==="
        elif wordclass is lujvo:
            print "===lujvo==="
        

        wordclassresults_parsed = parseResults(wordclassresults, term, wordclass)
        if testing:
            print "wordclassresults = " + str(wordclassresults)
            print "wordclassresults_parsed = " + str(wordclassresults_parsed)

        i = 0
        exactMatch = False
        while i < len(wordclassresults_parsed):
            for keyspot in wordclassresults_parsed[i]:
                if term == keyspot.strip():
                    notify.finalResult = term
                    if testing:
                        print "Final result: " + notify.finalResult
                    printOneResult(wordclassresults[i], wordclass, doflash=flash)
                    exactMatch = True
                    if testing:
                        print "Exact match for " + term
                    break
            i += 1

        if exactMatch == False:
            printResults(wordclassresults, wordclass)
            if flash:
                notify.noPerfectResult(term)
