#!/usr/bin/env python3
# fattr -- display file extended attributes (xattrs)
import argparse
import itertools
import os
import re
import sys

attr_aliases = {
    "author":       "user.dublincore.creator",
    "comment":      "user.xdg.comment",
    "date":         "user.dublincore.date",
    "desc":         "user.dublincore.description",
    "description":  "user.dublincore.description",
    "lang":         "user.xdg.language",
    "origin":       "user.xdg.origin.url",
    "mime":         "user.mime_type",
    "mimetype":     "user.mime_type",
    "publisher":    "user.dublincore.publisher",
    "referer":      "user.xdg.referrer.url",
    "relation":     "user.dublincore.relation",
    "subject":      "user.dublincore.subject",
    "title":        "user.dublincore.title",
}

# Hidden from default view
hidden_attrs = {
    "user.com.dropbox.attributes",
    "user.com.dropbox.attrs",
    "user.wine.sd",
}

def fmt_dosattrib_samba3(buf):
    # ~/src/samba/librpc/idl/xattr.idl
    import enum
    import io
    import struct

    class DosAttrib(enum.IntFlag):
        ReadOnly = 0x1
        Hidden = 0x2
        System = 0x4
        Volume = 0x8
        Directory = 0x10
        Archive = 0x20
        Device = 0x40
        Normal = 0x80
        Temporary = 0x100
        Sparse = 0x200
        ReparsePoint = 0x400
        Compressed = 0x800
        Offline = 0x1000
        NonIndexed = 0x2000
        Encrypted = 0x4000

    # Read legacy attrib_hex
    attrib, _, rest = buf.partition(b"\x00")
    if attrib:
        attrib = int(attrib, 16)
        attrib = DosAttrib(attrib)
        return f"({attrib._name_}) {buf}"
    else:
        print(f"fattr: XXX: attrib_hex not present in {buf!r}",
              file=sys.stderr)

    # Someday later
    #class ValidFlags(enum.IntFlag):
    #    Attrib = 0x1
    #    EaSize = 0x2
    #    Size = 0x4
    #    AllocSize = 0x8
    #    CreateTime = 0x10
    #    ChangeTime = 0x20
    #    ITime = 0x40
    #if buf:
    #    rd = io.BytesIO(buf)
    #    version = struct.unpack(">H", rd.read(2))
    #    print(f"{version = }")
    #    if version == 3:

    return buf

def show_attrs(file, *,
               follow=False,
               all_ns=False,
               _count=itertools.count()):
    if next(_count) > 0:
        print()
    print(f"\033[1m{file}\033[m")
    keys = os.listxattr(file, follow_symlinks=follow)
    if not args.all:
        keys = {k for k in keys if k.startswith("user.")}
        keys -= hidden_attrs
    #if not keys and not (args.all or args.empty):
    #    continue
    attrs = {key: os.getxattr(file, key, follow_symlinks=follow)
             for key in keys}
    if not attrs:
        print(f"  (No attributes.)")
    for key, value in sorted(attrs.items()):
        if key == "user.DOSATTRIB" and not args.all:
            value = fmt_dosattrib_samba3(value)
        if not args.all:
            key = key.removeprefix("user.")
        print(f"  {key} = {value}")

parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("--help", action="help",
                    help="show this help message and exit")
parser.add_argument("-a", "--all", action="store_true",
                    help="do not hide any attributes")
parser.add_argument("-h", "--no-dereference", action="store_true",
                    help="do not dereference symbolic links")
parser.add_argument("-r", "--recurse", action="store_true",
                    help="descend into directories")
parser.add_argument("-s", "--set", metavar="KEY=VALUE",
                    default=[], action="append",
                    help="set an attribute")
parser.add_argument("path", nargs="+")
args = parser.parse_args()

if args.set:
    exit("Not yet implemented")

sargs = dict(follow=not args.no_dereference,
             all_ns=args.all)

for path in args.path:
    show_attrs(path, **sargs)
    if os.path.isdir(path) and args.recurse:
        for dirpath, dirnames, filenames in os.walk(path):
            for name in dirnames + filenames:
                show_attrs(os.path.join(dirpath, name), **sargs)

#files = []
#attrs = {}
#
#for arg in sys.argv[1:]:
#    if m := re.match(r"^([^/?=]+)=(.*)$", arg):
#        key = m.group(1)
#        val = m.group(2)
#        if key in attr_aliases:
#            key = attr_aliases[key]
#        elif key[0] == ".":
#            key = key[1:]
#        elif not re.match(r"^(user|system|security|trusted)\.", key):
#            key = "user." + key
#        if key in attrs:
#            Core.warn("attribute %r seen multiple times", key)
#        attrs[key] = val
#    else:
#        files.append(arg)
#
#print(files)
#print(attrs)
#
#if attrs:
#    ...
#else:
#    ret = dump_attrs(files, args)
