#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import mutagen
import mutagen.id3
import mutagen.mp4
import os
import subprocess

# http://id3.org/id3v2.4.0-frames#4.1.
TRACK_UUID_ID3_OWNER_ID = 'symuid'
# freeform keys start with '----'
# http://mutagen.readthedocs.io/en/latest/api/mp4.html
TRACK_UUID_MP4_TAG = '----:symuid:uuid'


def generate_uuid():
    return subprocess.check_output(['uuid', '-v', '4', '-F', 'BIN']).strip()


def get_uuid_id3(id3_tags):
    assert isinstance(id3_tags, mutagen.id3.ID3), type(id3_tags)
    ufids = id3_tags.getall('UFID')
    for ufid in ufids:
        if ufid.owner == TRACK_UUID_ID3_OWNER_ID:
            return ufid.data
    return None


def get_or_assign_uuid_id3(id3_tags):
    uuid = get_uuid_id3(id3_tags)
    if uuid is None:
        # mutagen.id3._specs.EncodedTextSpec.write encodes 'owner'
        id3_tags.add(mutagen.id3.UFID(
            owner=TRACK_UUID_ID3_OWNER_ID,
            data=generate_uuid(),
        ))
        id3_tags.save()
        id3_tags.load(filename=id3_tags.filename)
        uuid = get_uuid_id3(id3_tags)
        print("{!r}: assigned uuid {!r}".format(id3_tags.filename, uuid))
        assert uuid is not None
    return uuid


def get_or_assign_uuid_mp4(mp4_file):
    if not TRACK_UUID_MP4_TAG in mp4_file:
        mp4_file[TRACK_UUID_MP4_TAG] = mutagen.mp4.MP4FreeForm(
            data=generate_uuid(),
            # https://mutagen.readthedocs.io/en/latest/api/mp4.html#mutagen.mp4.AtomDataType.UUID
            dataformat=mutagen.mp4.AtomDataType.UUID,
        )
        mp4_file.save()
        mp4_file.load(filename=mp4_file.filename)
        print("{!r}: assigned uuid {!r}".format(
            mp4_file.filename, mp4_file[TRACK_UUID_MP4_TAG][0],
        ))
    return mp4_file[TRACK_UUID_MP4_TAG][0]


def symuid_sync(path):
    if os.path.isdir(path):
        for dirpath, dirnames, filenames in os.walk(path):
            for filename in filenames:
                symuid_sync(os.path.join(dirpath, filename))
    else:
        try:
            f = mutagen.File(filename=path)
        except Exception:
            raise Exception(path)
        if not f:
            print("{!r}: unsupported filetype, ignored".format(path))
        elif isinstance(f, mutagen.mp4.MP4):
            get_or_assign_uuid_mp4(f)
        elif isinstance(f.tags, mutagen.id3.ID3):
            get_or_assign_uuid_id3(f.tags)
        else:
            raise Exception(f)


def _init_argparser():
    import argparse
    argparser = argparse.ArgumentParser(description=None)
    argparser.add_argument('path')
    return argparser


def main(argv):
    argparser = _init_argparser()
    args = argparser.parse_args(argv[1:])
    symuid_sync(**vars(args))
    return 0

if __name__ == "__main__":
    import sys
    sys.exit(main(sys.argv))