#!/usr/bin/env python

# Adapted from https://joris.kluivers.nl/blog/2014/02/10/storyboard-identifier-constants/ (https://github.com/kluivers/storyboard-constants)
# * Support for accessibility identifiers and accessibility labels
# * Easily extendable with xpath definitions
# * Valid identifiers
# * Namespaced constants, idea from https://www.mikeash.com/pyblog/friday-qa-2011-08-19-namespaced-constants-and-functions.html

PREFIX = ''
SPACING = '\t'

DEFINITIONS = {
    'SegueIdentifier':         [{'xpath': './/segue[@identifier]', 'attribute': 'identifier'}],
    'ControllerIdentifier':    [{'xpath': './/*[@storyboardIdentifier]', 'attribute': 'storyboardIdentifier'}],
    'ReuseIdentifier':         [{'xpath': './/*[@reuseIdentifier]', 'attribute': 'reuseIdentifier'}],
    'AccessibilityIdentifier': [{'xpath': './/accessibility[@identifier]', 'attribute': 'identifier'}, {'xpath': './/userDefinedRuntimeAttribute[@keyPath="accessibilityIdentifier"]', 'attribute': 'value'}],
    'AccessibilityLabel':      [{'xpath': './/accessibility[@label]', 'attribute': 'label'}, {'xpath': './/userDefinedRuntimeAttribute[@keyPath="accessibilityLabel"]', 'attribute': 'value'}],
}

import sys, os, re, unicodedata
import xml.etree.ElementTree as ElementTree

# From http://stackoverflow.com/questions/517923/what-is-the-best-way-to-remove-accents-in-a-python-unicode-string/518232#518232
def stripAccents(s):
    return ''.join(c for c in unicodedata.normalize('NFD', unicode(s)) if unicodedata.category(c) != 'Mn')

def makeIdentifier(s):
    return re.sub('[^\w]', '_', stripAccents(s))

def processStoryboard(storyboardPath, identifiers):
    root = ElementTree.parse(storyboardPath).getroot()
    storyboardName = os.path.splitext(storyboardPath)[0]
    for key,descriptors in DEFINITIONS.items():
        for descriptor in descriptors:
            identifiers[key].extend(map((lambda e: e.get(descriptor['attribute'])), root.findall(descriptor['xpath'])))
    
    return identifiers

def writeIdentifiers(path, identifiers, importHeader, structBegin, structContent, structEnd):
    with open(path, 'w+') as f:
        f.write('/* Generated file. DO NOT MODIFY */\n\n')
        f.write('#import {0}\n\n'.format(importHeader))
        
        for key,identifiers in identifiers.items():
            if len(identifiers) == 0:
                continue
            f.write(structBegin(key))
            for identifier in sorted(identifiers):
                f.write(structContent(identifier))
            f.write(structEnd(key))

def main(headerPath, implementationPath):
    identifiers = {key: [] for key in DEFINITIONS.keys()}
    for n in range(int(os.environ['SCRIPT_INPUT_FILE_COUNT'])):
        identifiers = processStoryboard(os.environ['SCRIPT_INPUT_FILE_' + str(n)], identifiers)
    
    writeIdentifiers(headerPath, identifiers, '<Foundation/Foundation.h>',
        (lambda kind:  'extern const struct {0}{1}Struct {{\n'.format(PREFIX, kind)),
        (lambda value: '{0}__unsafe_unretained NSString *{1};\n'.format(SPACING, makeIdentifier(value))),
        (lambda kind:  '}} {0}{1};\n\n'.format(PREFIX, kind)))
    writeIdentifiers(implementationPath, identifiers, '"' + os.path.basename(headerPath) + '"',
        (lambda kind:  'const struct {0}{1}Struct {0}{1} = {{\n'.format(PREFIX, kind)),
        (lambda value: '{0}.{1} = @"{2}",\n'.format(SPACING, makeIdentifier(value), value.encode('utf-8'))),
        (lambda kind:  '};\n\n'))

if __name__ == '__main__':
    if len(sys.argv) != 3:
        sys.exit('error: usage: {0} headerPath implementationPath'.format(os.path.basename(sys.argv[0])))
    
    main(sys.argv[1], sys.argv[2])