#!/usr/bin/env python
"""etym

Queries etymonline.com for word or phrase, and displays 
its etymology if found. Also supports looking up a random word.

Usage:
    etym [options] <word> ...
    etym [options] --random [--number=<num>]

Examples:
    etym viking
    etym --random
    etym -rn 7

Arguments:
    <word>      word (or phrase) to look up

Options:
    -h --help               Show this screen.
    -v --verbose            Verbose mode.
    -r --random             Look up random word.
    -n --number=<num>       Number of random words to look up [default: 1].
"""
import os
import sys
import re
import random
import requests
from textwrap import fill

from BeautifulSoup import BeautifulSoup
from blessings import Terminal
from docopt import docopt


def beautify(soup):
    # BeautifulSoup strips out whitespace around in-line markup tags, see 
    # http://stackoverflow.com/a/16767636 for explanation of solution used below.
    beautifiedText = str()
    term = Terminal()
    for i in soup:
        if i.string:
            if re.match(r'<span class="foreign">', str(i), re.UNICODE):
                i.string = re.sub(r'<span class="foreign">(.+)</span>', r'{t.standout}\1{t.normal}'.format(t=term), str(i))
            beautifiedText += ' ' + i.string

    # Clean up 
    beautifiedText = re.sub('^\s+', '', beautifiedText)
    beautifiedText = re.sub('\s{2,}', ' ', beautifiedText)
    beautifiedText = re.sub('\s+([,)\].;:])', '\g<1>', beautifiedText)
    beautifiedText = re.sub('([(])\s+', '\g<1>', beautifiedText)
    return beautifiedText

def getRandomWord(): 
    dictFile = "/usr/share/dict/words"
    assert os.path.exists(dictFile), "Could not find dictionary file at %s." % dictFile
    candidate = random.choice(open(dictFile).readlines()).rstrip('\n')
    return candidate

class NoResultsFound(Exception):
    def __init__(self, value):
        self.value = "Found no hits for query '%s'." % value

    def __str__(self):
        return repr(self.value)

def queryEtymOnline(query, verbose=None):

    if verbose:
        sys.stdout.write("Querying etymonline.com for '%s'... " % query)

    r = requests.get("http://www.etymonline.com/index.php?search=" + query)
    soup = BeautifulSoup(r.content, convertEntities=BeautifulSoup.HTML_ENTITIES)

    try:
        hit = soup.dt.a.text
    except:
        raise NoResultsFound(query)

    if verbose:
        print "OK"

    etymology = beautify(soup.dd)
    return (hit, etymology)

def performLookup(query, verbose=None, random=None):

    for attempts in range(5):
        try:
            (hit, etymology) = queryEtymOnline(query, verbose=verbose)

        except NoResultsFound:
            if verbose:
                print "FAIL"
            if random:
                query = getRandomWord()
                continue
            else:
                sys.exit("No etymology found for '%s'." % query)

        except requests.exceptions.ConnectionError:
            sys.exit("Could not query etymonline.com; check internet connection.")

        break

    return (hit, etymology)

def displayResults(hit, etymology):
    t = Terminal()
    print t.bold(hit)
    print fill(etymology, width=t.width)

def main(args):

    if args['<word>']:
        query = ' '.join(args['<word>'])

    if args['--random']:
    
        attempts = int(args['--number'])

        while attempts > 0:
            query = getRandomWord()
            (hit, etymology) = performLookup(query, verbose=args['--verbose'], random=args['--random'])
            displayResults(hit, etymology)
            # Pad the entries with a line break, for better readability 
            # when displaying multiple results.
            attempts -= 1
            if attempts > 0:
                print ''

    else:
        (hit, etymology) = performLookup(query, verbose=args['--verbose'], random=args['--random'])
        displayResults(hit, etymology)

if __name__ == '__main__':
    arguments = docopt(__doc__, version='etym v0.1')
    main(arguments)
