#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright 2014 Cédric Picard
#
# LICENSE
# This program 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 3 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, see <http://www.gnu.org/licenses/>.
# END_OF_LICENSE
#
"""
Simple command line browser independant bookmark utility.

Usage: bm [options] [-r] URL TAG...
       bm [options]  -d  URL
       bm [options]  -l  [TAG]...
       bm [options]  -L  TAG...
       bm [options]  URL
       bm [options]  -i  SOURCE...
       bm [options]  -t

Arguments:
    URL     The url to bookmark
            If alone, print the tags associated with URL
            If the url corresponds to an existing file,
            the absolute path is substituted to URL
            If URL is '-', then the program looks for a list of URL
            comming from the standard input.
    TAG     The tags to use with the url.
    SOURCE  When uniting, the paths to the source files.

Options:
    -h, --help          Print this help and exit
    -r, --remove        Remove TAG from URL
    -d, --delete        Delete an url from the database
    -l, --list-every    List the urls with every of TAG
                        If no tag is given, list all urls
    -L, --list-any      List the urls with any of TAG
    -f, --file FILE     Use FILE as the database
                        Default is ~/.bookmarks
    -t, --tags          List every tag present in the database
                        with how many times it is used.
                        Output is sorted from the least to the most used
    -i, --import        Import bookmarks from sources into the database.
    -n, --no-path-subs  Disable file path substitution
    --version           Print current version number
"""
VERSION = "1.2.1"

import os
from msgpack import dump, load
from msgpack.exceptions import UnpackValueError
from docopt import docopt


def add(database, url, tags):
    if url in database:
        for tag in tags:
            if tag not in database[url]:
                database[url].append(tag)
    else:
        database[url] = list(tags)


def remove(database, url, tags):
    try:
        for tag in tags:
            database[url].remove(tag)

        if database[url] == []:
            database.pop(url)
    except ValueError:
        pass


def delete(database, url):
    try:
        database.pop(url)
    except KeyError:
        pass


def list_any(database, tags):
    for url in database:
        if set(tags).intersection(set(database[url])) != set() or tags == []:
            yield url


def list_every(database, tags):
    for url in database:
        if set(tags).issubset(set(database[url])):
            yield url


def list_every_tags(database):
    tags = {}
    for each in database:
        for tag in database[each]:
            try:
                tags[tag] += 1
            except KeyError:
                tags[tag] = 1
    return [(tags[x], x) for x in tags]


def _list(database, urls, tags, method):
    result = []
    for url in urls:
        result += list(method(database, tags))

    result.sort()
    return result


def import_db(database, tags):
    paths = expand_paths(tags)

    for path in paths:
        db = open_db(path)
        for url in db:
            add(database, url, db[url])


def manage_urls(urls, tags, d_file, database, args):
    if args["--delete"]:
        for url in urls:
            delete(database, url)
        dump(database, open(d_file, 'wb'))

    elif args["--list-any"]:
        for each in _list(database, urls, tags, list_any):
            print(each)

    elif args["--list-every"]:
        for each in _list(database, urls, tags, list_every):
            print(each)

    elif args["--remove"]:
        for url in urls:
            remove(database, url, tags)
        dump(database, open(d_file, 'wb'))

    elif args["--tags"]:
        result = list_every_tags(database)
        result.sort()
        for num, tag in result:
            print("%s %s" % (tag, num))

    elif args["--import"]:
        for url in urls:
            import_db(database, url, tags)
        dump(database, open(d_file, 'wb'))

    elif args["TAG"]:
        for url in urls:
            add(database, url, tags)
        dump(database, open(d_file, 'wb'))

    else:
        for url in urls:
            for tag in database[url]:
                print(tag)


def expand_paths(paths):
    for path in paths:
        if path is not None and os.path.exists(path):
            yield os.path.abspath(path)

        else:
            yield path


def open_db(path):
    return load(open(path, "rb"), encoding="utf-8")


def main():
    args = docopt(__doc__, version=VERSION)

    tags = args["TAG"] or args["SOURCE"]

    if args["URL"] == '-':
        urls = os.sys.stdin.read().splitlines()

    else:
        urls = [ args["URL"] ]

    d_file = args["--file"] or os.environ["HOME"] + "/.bookmarks"
    try:
        if not os.path.exists(d_file):
            print('The file "' + d_file + '" does not exist: creating it.',
                    file=os.sys.stderr)
            open(d_file, "a+").close()

        database = open_db(d_file)

    except UnpackValueError:
        database = {}

    except PermissionError as e:
        os.sys.exit(e)

    if not args["--no-path-subs"]:
        urls = expand_paths(urls)

    manage_urls(urls, tags, d_file, database, args)


if __name__ == "__main__":
    try:
        main()
    except KeyError as e:
        print("No such bookmark:", e, file=os.sys.stderr)
        os.sys.exit(1)
