#!/usr/bin/python

# Copyright 2013 Chiraag M. Nataraj
# 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/>.

import sys
import argparse
import xattr
import os

parser = argparse.ArgumentParser(description="%(prog)s is a small script to tag files")
parser.add_argument('filename', help="File to parse", default="", nargs="?")
parser.add_argument('tags', nargs=argparse.REMAINDER, help="Tags to add")
parser.add_argument('-d', '--delete', action="store_true", default=False, help="Delete all tags")
parser.add_argument('-r', '--remove', metavar="TAG", help="Remove a specific tag", default="");
# parser.add_argument('-l', '--list', dest="view", action="store_true", default=False, help="List all tags for filename")
parser.add_argument('-L', '--list-tags', dest="view_tags", action="store_true", default=False, help="List all tags in the database")
parser.add_argument('-l', '--list', dest="search", metavar="TAG", help="Search for files and folders with a specific tag", default="")

def error(string, err):
    '''
    Prints out an error message and exits with a exit status of err
    '''
    sys.stderr.write(string + "\n")
    sys.exit(err)

def tagExists(filename):
    '''
    Figures out whether a file contains tags
    '''
    return "user.tags" in xattr.listxattr(filename) or b'user.tags' in xattr.listxattr(filename)

def view(filename):
    '''
    Prints all the tags for a filename
    '''
    if tagExists(filename):
        tags = xattr.getxattr(filename, "user.tags").split(",")
        for i in tags:
            print(i)

def add(filename, tags, tag_dict):
    '''
    Adds tags to the file and adds the file to the dictionary containing the
    tags and the files with those tags
    '''
    if tagExists(filename):
        toWrite = xattr.getxattr(filename, "user.tags") + ","
    else:
        toWrite = ""
    tagDups = []
    newTags = []
    toWriteTempList = toWrite.split(",")
    toWriteTempList = [x for x in toWriteTempList if x]
    for i in toWriteTempList:
        tagDups.append(i)
    for i in range(len(tags) - 1):
        if tags[i] not in tagDups:
            toWrite += tags[i] + ","
            newTags.append(tags[i])
            tagDups.append(tags[i])
    if tags[len(tags) - 1] not in tagDups:
        toWrite += tags[len(tags) - 1]
        newTags.append(tags[len(tags) - 1])
    if toWrite.endswith(","):
        toWrite = toWrite[0:len(toWrite) - 1]
    xattr.setxattr(filename, "user.tags", toWrite)
    for i in newTags:
        if(i not in tag_dict):
            tag_dict[i] = [filename]
        else:
            tag_dict[i].append(filename)

def delete(filename, quiet, tag_dict):
    if tagExists(filename):
        xattr.removexattr(filename, "user.tags")
        for key in tag_dict:
            if filename in tag_dict[key]:
                tag_dict[key].remove(filename)
        if not quiet:
            print("All tags successfully removed!")
    else:
        if not quiet:
            print("No tags!")

def remove(filename, tag, tag_dict):
    if tagExists(filename):
        tempTags = xattr.getxattr(filename, "user.tags")
        tags = tempTags.split(",")
        while tag in tags:
            tags.remove(tag)
        delete(filename, True, tag_dict)
        if len(tags) > 0:
            add(filename, tags, tag_dict)
        print("Removed " + tag + " from " + filename)
    else:
        print("No tags!")

def viewKeys(tag_dict):
    keys = tag_dict.keys()
    for i in keys:
        print i

def search(key, tag_dict):
    if key in tag_dict:
        lst = tag_dict[key]
        for i in lst:
            print i
    else:
        print 'No matches found!'

def clean_dict(tag_dict):
    keys = []
    for key in tag_dict:
        if tag_dict[key] == []:
            keys.append(key)
    for key in keys:
        tag_dict.pop(key)

def main():
    args = parser.parse_args()
    path = os.path.join(os.getenv("HOME"), ".config", "tag", "tag-dict")
    directory = os.path.dirname(path)
    if(os.path.isfile(path)):
        dict_file = open(path, "r")
        tag_dict = (dict)(eval(dict_file.read(), {}, {}))
        dict_file.close()
    else:
        os.makedirs(directory)
        tag_dict = {}
    args.view = True
    if (not args.delete and args.filename == '' and args.remove == '' and args.search == '' and args.tags == [] and not args.view_tags):
        parser.print_help()
    if (args.delete or (args.remove != "")) and (args.tags != []):
        error("Can't delete and set tags at the same time!", 1)
    if args.delete and (args.remove != ""):
        error("Can't simultaneously delete certain tags and all tags!", 2)
    if (args.delete or (args.remove != "") or (args.tags != [])) and args.filename == "":
        error("Filename must be specified for deleting, removing, and adding tags!", 3)
    if (args.delete or (args.remove != "") or (args.tags != []) or args.view_tags or (args.search != "")):
        args.view = False
    if args.view:
        view(os.path.abspath(args.filename))
    if args.view_tags:
        viewKeys(tag_dict)
    if args.search != "":
        search(args.search, tag_dict)
    if args.delete:
        delete(os.path.abspath(args.filename), False, tag_dict)
    elif args.remove != "":
        remove(os.path.abspath(args.filename), args.remove, tag_dict)
    elif args.tags != []:
        add(os.path.abspath(args.filename), args.tags, tag_dict)
    clean_dict(tag_dict)
    dict_file = open(path, "w")
    dict_file.write(str(tag_dict))
    dict_file.close()

if __name__ == "__main__":
    main()
