#!/usr/bin/python
#
# Command line bookmark tool
# Copyright 2008, 2009 Bart Spaans


import os
import sys
import pipes
from subprocess import call
from btools.pybookmark import *
import btools.common as common


name = "bm"
description = "Command line bookmarking tool"

commands = [(["-a", "--add"], "[DIRECTORY] [TAG]",
             "Adds DIRECTORY under TAG. If no directory is given this command is "
             "the same as --add-current."),
            (["--add-current", "--current"], "[TAG]",
             "Adds the current directory. If no arguments are given the tag will be set to "
             "the top level directory automatically."),
            (["-c", "--clear"], "", "Clear the index."),
            (["-d", "-r", "--delete", "--remove"], "TAG",
             "Remove entry matching tag from the index. If no TAG is given, the top level "
             "directory will be used."),
            (["-da", "-ra", "--delete-all-matching", 
              "--remove-all-matching"], "TAG", "Remove all entries matching TAG."),
            (["-e", "--export"], "[FILE]", "Writes a shell script that can be used to reproduce the bookmarks and directories. If FILE is not given, the script will be written to stdout."),
            (["-h", "--help"], "[COMMANDS]", "Output help. If COMMANDS are given, output only the help for that command"),
            (["-l", "--list"], "", "List the whole index. Default action."),
            (["-m", "--match"], "TAG", "Output all entries that match TAG on prefix."),
            (["-s", "--symlink"], "[DEST_DIR]", "Turns bookmarks into symbolic links. Makes them in the current directory if no DEST_DIR is given."),
            (["-sd", "--sort-dirs"], "", "Sort index on directory and save"),
            (["-st", "--sort-tags"], "", "Sort index on tags and save."),
            (["-v", "--version"], "", "Output version information"),
            (["-?"], "", "Output location of the bookmark file."),
            ]
usage = [ ("[INDEX]", "Return entry at index"),
          ("[TAG]", "Return entry with TAG"),
          ("[TAG] --exec [COMMAND]", "Executes COMMAND. Use {} to get bookmark."),
          "[OPTION] [ARGUMENTS]", ]

examples = [("-a test", "Adds current directory under name test." ),
        ("-a ../ test2", "Adds parent directory under test2."),
        ("test", "Returns directory tagged test."),
        ("0", "Jump to the first entry."),
        ("test --exec ls -al {}", "Finds directory tagged test, and lists its contents."),
        ("-m c", "Outputs all the entries beginning with a c."),]

# Front end

def cli_display():
    file = get_conf_location()
    if not common.OUTPUT_COLOR:
        return display_bookmarks(file)
    else:
        return display_colored_bookmarks(file)

def cli_add(args):
    """Add directory to the bookmarks. This function is equivalent to 
    cli_add_current if args is empty or the first argument doesn't 
    specify a directory."""

    if args == []:
        return cli_add_current([])

    if not os.path.isdir(args[0]):
        return cli_add_current(args)

    dir = os.path.realpath(args[0])
    tag = " ".join(args[1:])
    if tag == "":
        tag = os.path.basename(dir)
    add_bookmark(get_conf_location(), tag, dir)
    cli_display()

    

def cli_add_current(args):
    """Add the current directory. 
    If called without arguments the top-level directory name will be used as tag."""
    dir = os.getcwd()
    if args == []:
        tag = os.path.basename(dir)
    else:
        tag = " ".join(args)
    add_bookmark(get_conf_location(), tag, dir)
    cli_display()
 

def cli_export(args = []):
    home = ""
    if "HOME" in os.environ:
        home = os.environ["HOME"]
    
    if args == []:
        out = sys.stdout
    else:
        if len(args) == 1:
            try:
                out = open(args[0], "wb")
            except:
                common.error("Couldn't open '%s' for reading" % args[0])
            os.fchmod(out.fileno(), 0755)
        else:
            common.error("Expecting only one file argument after export command")

    out.write("# btools - Bookmark environment\n")

    homeUsed = False
    env = read_bookmarks(get_conf_location())
    for bm, dir in env:
        if dir.startswith(home) and home != "":
            if not homeUsed:
                out.write("if [ ! $HOME ] ; then \n")
                out.write("    echo %s\n" % pipes.quote("Warning: HOME environment variable not set. Using original directory: %s" % home))
                out.write("    HOME=%s\n" % pipes.quote(home))
                out.write("fi\n\n")

            dir = "$HOME" + dir[len(home):]
            homeUsed = True
        bm, dir = pipes.quote(bm), pipes.quote(dir) 
        out.write("mkbm %s %s\n" % (bm, dir))




def get_exec_index(args):
    for i, x in enumerate(args):
        if x in ["--exec", "-e", "--execute"]:
            return i
    return -1


def cli_print_bookmark(args):
    """Print bookmark matching if and only if one tag is matching. Execute command if given."""
    execargs = []
    execi = get_exec_index(args)
    if execi > -1:
        execargs = args[execi + 1:]
        args = args[:execi]

    b = get_bookmark(" ".join(args), read_bookmarks(get_conf_location()))

    # Check exact match    
    if len(b) > 1:
        for bm, dest in b:
            if bm == " ".join(args):
                b = [(bm, dest)]

    if len(b) == 1:
        if "{}" in execargs:
            execargs[execargs.index("{}")] = "\"%s\"" % b[0][1].replace("'", "\'")
        if execargs != []:
            print " ".join(execargs)
            call(execargs, shell = True)
        else:
            print b[0][1]
    else:
        sys.exit(1)


def cli_print_all_matching_bookmarks(args):
    """Output all matching tags. Execute command if given."""
    execargs = []
    execi = get_exec_index(args)
    if execi > -1:
        execargs = args[execi + 1:]
        args = args[:execi]
    for tag, dest in get_bookmark(" ".join(args), read_bookmarks(get_conf_location())):
        if "{}" in execargs:
            i = execargs.index("{}")
            tmp = execargs[i]
            execargs[i] = "\"%s\"" % dest
            call(execargs)
            execargs[i] = tmp
        else:
            print "%s [%s]" % (dest, tag)


def cli_clear(args):
    """Clear the bookmark index."""
    r = raw_input("Are you sure you want to clear your bookmark file [No/yes]? ")
    if str.lower(r) in ["y", "ye", "yes", "yeah", "yaaar", "aye"]:
        print "Ok, let's do this thing...",
        clear_bookmarks(get_conf_location())
        print "DONE"
    elif r == "Open the pod bay doors, HAL":
        print "I'm sorry, Dave. I'm afraid I can't do that."
    else:
        print "Phew!"


def cli_remove(args):
    """Remove only matching tag from the index."""

    if args == []:
        dir = os.getcwd()
        args = [os.path.basename(dir)]
 
    bms = read_bookmarks(get_conf_location())
    b = get_bookmark(" ".join(args), bms)

    if len(b) > 1:
        for bm, dest in b:
            if bm == " ".join(args):
                b = [(bm, dest)]

    if len(b) == 1:
        bms.remove(b[0])
        write_bookmarks(get_conf_location(), bms)
        cli_display()
    else:
        common.error("Could not remove bookmark %s. Matched %d directories." % (" ".join(args), len(b)))

def cli_remove_all_matching(args):
    """Remove all matching tags from the index."""
    if args == []:
        return cli_clear([])
    bms = read_bookmarks(get_conf_location())
    res = list(bms)
    for b in  get_bookmark(" ".join(args), bms):
        res.remove(b)
    write_bookmarks(get_conf_location(), res)
    cli_display()

def cli_symlink(args):
    if args == []:
        dir = "."
    else:
        dir = " ".join(args)

    if os.path.isdir(dir):
        dir = os.path.realpath(dir)
        bookmarks_to_symlinks(read_bookmarks(get_conf_location()) , dir)
    else:
        common.error("Not a valid directory: %s" % dir)

def cli_sort_dirs():
    bms = sorted(read_bookmarks(get_conf_location()), key=lambda x: x[1])
    write_bookmarks( get_conf_location(), bms)
    cli_display()

def cli_sort_tags():
    bms = sorted(read_bookmarks(get_conf_location()), key=lambda x: x[0])
    write_bookmarks( get_conf_location(), bms)
    cli_display()

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


def command_line_interface():
    if len(sys.argv) == 1:
        return cli_display()
    command = str.lower(sys.argv[1])
    args = sys.argv[2:]

    if command in ["-a", "--add"]:
        cli_add(args)
    elif command in ["--add-current", "--current"]:
        cli_add_current(args)
    elif command in ["-d", "-r", "--delete", "--remove"]:
        cli_remove(args)
    elif command in ["-da", "-ra", "--delete-all-matching", "--remove-all-matching"]:
        cli_remove_all_matching(args)
    elif command in ["-e", "--export"]:
        cli_export(args)
    elif command in ["-l", "--list"]:
        cli_display()
    elif command in ["-c", "--clear"]:
        cli_clear(args)
    elif command in ["-h", "--help"]:
        if args == []:
            cli_help()
        else:
            common.cli_command_help(args, commands)
    elif command in ["-m", "--match", "--match-any"] and len(args) > 0:
        cli_print_all_matching_bookmarks(args)
    elif command in ["-m", "-la", "--list-all"]:
        cli_print_all_matching_bookmarks([""])
    elif command in ["-s", "--symlink"]:
        cli_symlink(args)
    elif command in ["-sd", "--sort-dirs"]:
        cli_sort_dirs()
    elif command in ["-st", "--sort-tags"]:
        cli_sort_tags()
    elif command in ["-?", "--bookmark-file"]:
        print get_conf_location()
    elif command in ["--version", "-v"]:
        common.cli_version(name, description)
    elif command[0] != "-":
        cli_print_bookmark(sys.argv[1:])
    else:
        print "Command line option '%s' not known." % command

if __name__ == "__main__":
    command_line_interface()
