#!/usr/bin/env python
#
# Suggest a bookmark.
# Looks in index first and does a naive lookup,
# then uses regular expressions to get the best result.

import os
import sys
import re
import subprocess

from btools import common

common.loglevel = 2
pattern_collections = common.pattern_collections

_cache = {}

name = "bmsuggest"
description = "Suggests bookmarks for file names."
usage = ["[FILES] [OPTIONS]"]
commands = [
    (["-b", "--bookmark-exec"], "COMMAND", 
     "Executes shell COMMAND to refine best bookmark. Variables %file%, "
     "%filepath% and %bookmark% will be replaced."),
    (["-d", "--defaults"], "", "Output default pattern collections"),
    (["-e", "--exec"], "COMMAND", "Executes shell COMMAND after each match. "
     "Variables %file%, %filepath% and %bookmark% will be replaced"),
    (["-h", "--help"], "", "This help screen"),
    (["-l", "--load"], "DEFAULT", "Load one of the default collections"),
    (["-p", "--patterns"], "FILE", "Use patterns from FILE"),
    ]

examples = [("test.avi -l movies", "Suggest bookmark based on movie patterns"),
            ("test.avi -l movies --exec \"echo %filepath% %bookmark%\"", 
             "Execute statement for best suggestion"),
            ("test.avi -l movies --exec \"mvbm %filepath% -p . %bookmark%\"",
             "Moves test.avi to the best suggestion. The bookmark will be created if it doesn't exist.") ]

def get_patterns_from_file(file):
    
    if _cache.has_key(file):
        return _cache[file]
 
    common.info("Loading pattern file %s" % file)
    f = open(file, "r")
    con = [ x.split("-") for x in f.read().splitlines() ]
    _cache[file] = con
    common.info("%d patterns loaded from file %s" % (len(con), file))
    return con

def get_text_matches(search, file):
    
    # Read a file made out of bookmarks
    con = get_patterns_from_file(file)
    common.info("Finding text matches for %s in %s" % (search, file))
    f = open(file, "r")
    results = []
    for bm in con:
        if all([ b in search for b in bm ]):
            m = "-".join(bm)
            common.debug("Found text match for %s: %s" % (search, m))
            results.append(m)
    f.close()

    common.info("%d text matches" % len(results))
    return results

def prepare_regex(line):
    escape = ["(", ")", "*", "?", "+", ".", "[", "]"]
    l = line
    for x in escape:
        l = l.replace(x, "\\" + x)
    l = ".*" + l.replace("-", ".*") + ".*"
    return l

def get_best(results, search):

    common.info("Doing regex search for %s in text matches" % search)
    r = []
    for res in results:
        reg = prepare_regex(res)
        if re.match(reg, search, re.IGNORECASE):
            common.debug("%s matches" % res)
            r.append(res)
        else:
            common.debug("%s doesn't match" % res)

    common.info("%d regex matches" % len(r))
    common.info("Finding longest match")
    n = -1
    best = ""
    for res in r:
        if len(res) > n:
            common.debug("Longest is currently: %s" % res)
            best = res
            n = len(res)
    common.info("Best match is: %s" % best)
    return best

def cli_match(files, options):

    for file in files:
        x = file.lower()
        res = get_text_matches(x, options["pattern_file"])
        best = get_best(res, x)
        if options["exec_bm"] != "" and best != "":
            e = options["exec_bm"]
            common.info("Executing bm_exec hook: %s" % e)
            hook = common.replace_variables(e, {"file": file,
                                                "filepath" : os.path.realpath(x),
                                                "bookmark": best })
            new = subprocess.Popen(hook, shell = True, stdout = subprocess.PIPE).communicate()
            if new[0] != "":
                n = new[0].splitlines()[0]
                best = n
                common.debug("Changed best bookmark to %s" % best)
            else:
                common.error("bm-exec argument didn't return a new bookmark")
        print file, best
        if options["exec_hook"] != "" and best != "":
            e = options["exec_hook"]
            hook = common.replace_variables(e, {"file": file,
                                                "filepath" : os.path.realpath(x),
                                                "bookmark": best })
            common.info("Executing exec hook: %s" % hook)
            subprocess.Popen(hook, shell = True).wait()


def cli_help():
    common.cli_module_help(globals())

def cli_default_patterns():
    for x in pattern_collections.keys():
        print x

def parse_options(args, options):
    argumentsPending = False
    last = ""
    res = options

    for a in args:
        if not argumentsPending:
            if a in ["-h", "--help"]:
                cli_help()
                sys.exit(0)
            elif a in ["-p", "--patterns"]:
                last = "pattern_file"
                argumentsPending = True
            elif a in ["-d", "--defaults"]:
                cli_default_patterns()
                sys.exit(0)
            elif a in ["-l", "--load"]:
                last = "load_default_pattern"
                argumentsPending = True
            elif a in ["-e", "--exec"]:
                last = "exec_hook"
                argumentsPending = True
            elif a in ["-b", "--bookmark-exec"]:
                last = "exec_bm"
                argumentsPending = True
            else:
                common.warning("Unknown option %s" % a)
        else:
            if last in ["pattern_file", "exec_hook", "exec_bm"]:
                res[last] = a
                argumentsPending = False
            elif last == "load_default_pattern":
                if pattern_collections.has_key(a):
                    res["pattern_file"] = pattern_collections[a]
                    argumentsPending = False
                else:
                    common.error("Unknown pattern collection %s" % a)
    if argumentsPending:
        common.error("Expecting arguments")
    return res

   
def command_line_interface():
    args = sys.argv

    if len(args) == 1:
        return cli_help()

    files = []
    inOptions = False
    options = {"pattern_file": "/usr/share/bm/movies.patterns",
               "exec_hook": "",
               "exec_bm" : ""}
    for i, a in enumerate(args):
        if a[0] == "-":
            inOptions = True
            options = parse_options(args[i:], options)
            break
        if not inOptions and i != 0:
            if os.path.isfile(a):
                files.append(a)
            else:
                common.warning("Ignoring %s. Not a file." % a)

    cli_match(files, options)

if __name__ == '__main__':
    command_line_interface()
