#!/usr/bin/python
# Update source code to match icons listed in pixmaps/Makefile.am
import re, os, sys, subprocess

makefile = 'pixmaps/Makefile.am'
doc_makefile = 'devel-docs/libgwyui/Makefile.am'
base = 'libgwyui/icons'
editorfile = 'gwyddion/toolbox-editor.c'
indextheme = 'pixmaps/index.theme'

def read_makefile_icons(target='list-pkg-icons'):
    """Read image list by running make list-pkg-icons."""
    icon_re = re.compile(r'^(?P<size>\d+)x(?P=size)/(?P<type>[a-zA-Z-]+)/(?P<name>.*?)\.(?P<format>png|svg)$')
    icons = {}
    examples = {}
    directories = set()
    command = ['make', '-C', 'pixmaps', target]
    lst = subprocess.check_output(command)
    if type(lst) is not str:
        lst = lst.decode('utf-8')
    for line in lst.split('\n'):
        line = line.strip()
        if not line.startswith('ICONLIST'):
            continue
        for icon in line.split()[1:]:
            m = icon_re.search(icon)
            if m:
                name, size, typ = m.group('name'), m.group('size'), m.group('type')
                size = int(size)
                icons.setdefault(name, [])
                icons[name].append(size)
                if size == 24 or name not in examples or (examples[name][1] != 24 and size > examples[name][1] != 24):
                    examples[name] = icon, size
                directories.add((size, typ))
    return icons, directories, examples

def read_since(f):
    """Read `Since' notes from Makefile"""
    sinces = {}
    for line in open(f):
        m = re.match(r'^\s*#\s+Since\s+(?P<version>[0-9.]+):\s*(?P<list>[-a-z0-9_. ]+)', line)
        if not m:
            continue
        for i in m.group('list').split():
            assert i not in sinces
            sinces[i] = m.group('version')
    return sinces

def replace_file(f, replacement, commentstyle='C'):
    if type(replacement) is not str:
        replacement = replacement.decode('utf-8')

    if commentstyle == 'C':
        cbegin = r'(/\* @@@ GENERATED ICON LIST BEGIN @@@ \*/\n)'
        cend = r'(/\* @@@ GENERATED ICON LIST END @@@ \*/\n)'
    elif commentstyle is None:
        pass
    else:
        assert not 'reached'

    oldcontent = open(f).read()
    if commentstyle is None:
        newcontent = replacement
    else:
        newcontent = re.sub(r'(?s)' + cbegin + r'(.*)' + cend, r'\g<1>' + replacement + r'\g<3>', oldcontent)
    # Don't waste time running diff in the trivial case
    if oldcontent == newcontent:
        return

    xgen = '%s.xgen' % f
    open(xgen, 'w').write(newcontent)
    # FIXME: status interpretation is system-dependent
    status = os.system('diff -u %s %s' % (f, xgen)) >> 8
    if status == 1:
        sys.stderr.write('%s: Updating %s\n' % (sys.argv[0], f))
        open(f, 'w').write(newcontent)
    elif status > 1:
        sys.stderr.write('%s: Diff failed for %s\n' % (sys.argv[0], f))
    os.unlink(xgen)

def nogwy(x):
    return re.sub(r'^GWY_', '', x)

def update_macros(icons):
    """Update header file with icon macro definitions."""

    # Format #defines
    hfile = base + '.h'
    defines = ['#define GWY_ICON_%s "%s"\n' % (nogwy(x.upper()), x) for x in sorted(icons.keys())]
    p = subprocess.Popen('./utils/align-declarations.py',
                         stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True)
    defines, perr = p.communicate(''.join(defines).encode('utf-8'))
    replace_file(hfile, defines)

def update_documentation(icons, sinces):
    """Update `documentation' in C source to list all icons."""

    # Format documentation entries
    # FIXME GTK3: Showing icons in documentation no longer works because we now have icons in all sorts of
    # places.
    cfile = base + '.c'
    template = ('/**\n'
                ' * GWY_ICON_%s:\n'
                ' *\n'
                ' * The "%s" icon.\n'
                ' * <inlinegraphic fileref="%s.svg" format="SVG"/>\n'
                '%s'
                ' **/\n'
                '\n')
    docs = []
    for k, v in sorted(icons.items()):
        words = re.sub(r'_1_1', '_1:1', k).split('_')
        for i in range(len(words)):
            # Heuristics: words without wovels are abbreviations
            if not re.search(r'[aeiouy]', words[i]):
                words[i] = words[i].upper()
            else:
                words[i] = words[i].title()
        human_name = '-'.join(words)

        if 24 in v:
            size = 24
        else:
            size = max(v)

        if k in sinces:
            s = ' *\n * Since: %s\n' % sinces[k]
            del sinces[k]
        else:
            s = ''

        docs.append(template % (nogwy(k.upper()), human_name, k, s))
    docs = ''.join(docs)
    replace_file(cfile, docs)

def update_gtkdoc_makefile(examples):
    text = open(doc_makefile).read()
    prefix = '$(top_srcdir)/pixmaps/'
    iconlist = ' \\\n	'.join(sorted(prefix + v[0] for v in examples.values()))
    text = re.sub(r'(?ms)^GWY_STOCK_ICONS\s*=.*?\n\n', r'GWY_STOCK_ICONS = ' + iconlist + '\n\n', text)
    open(doc_makefile, 'w').write(text)

def update_editor(icons):
    """Update toolbox editor file with icon list."""

    # Format #defines
    names = ['    GWY_ICON_%s,\n' % (nogwy(x.upper())) for x in sorted(icons.keys())]
    names = ''.join(names)
    replace_file(editorfile, names)

def update_index_theme(icondirs):
    """Update private index.theme file with icon directory list."""

    namemap = {'apps': 'Applications'}
    template = ('[Icon Theme]\n'
                'Name=Hicolor\n'
                'Comment=Gwyddion-specific icons\n'
                'Hidden=true\n'
                'Directories=%s\n'
                '\n')
    # FIXME: One day we will use SVG icons directly (although they will still be fixed).
    dirtemplate = ('[%s]\n'
                   'Size=%u\n'
                   'Context=%s\n'
                   'Type=fixed\n'
                   '\n')

    blocks = [None]
    dirnames = []
    for size, typ in sorted(icondirs):
        context = namemap.get(typ, typ.title())
        dirname = '%ux%u/%s' % (size, size, typ)
        blocks.append(dirtemplate % (dirname, size, context))
        dirnames.append(dirname)

    blocks[0] = template % ','.join(dirnames)
    replace_file(indextheme, ''.join(blocks), commentstyle=None)

icons, icondirs, examples = read_makefile_icons()
gwyicons = dict((k, v) for k, v in icons.items() if not k.startswith('gtk-'))

sincs = read_since(makefile)
update_macros(gwyicons)
update_documentation(gwyicons, sincs)
update_editor(gwyicons)
update_index_theme(icondirs)
update_gtkdoc_makefile(examples)
# Check for unused since declarations, they are typos
if sincs:
    sys.stdout.write('Unused since: %s\n' % str(sincs))
    sys.exit(1)

