"""
Python script to list contents of template file as indented list.

At some point, might get expanded to also list out attributes.


For future reference, to run from template directory
  LD_LIBRARY_PATH=/media/ssd/sim/cmb_core/build/CMBSuperBuild/install/lib \
  /media/ssd/sim/cmb_core/build/CMBSuperBuild/install/bin/pvpython \
  /media/ssd/sim/cmb_core/git/SMTK/utilities/python/listdefs.py \
  -t GEOTACS_Template_3.0.sbt
"""


# Global vars
app_description = 'Python script to list definitions'
default_spaces_per_indent = 2

import argparse
import sys

try:
    import smtk
except ImportError:
    print
    print app_description
    print 'Not able to import smtk library. You might need to:'
    print '  - Use the PYTHONPATH variable to point to the smtk python lib'
    print
    sys.exit(-1)


def get_base_definitions(defn, def_list):
    '''Recursively builds list of base definitions

    '''
    base_def = defn.baseDefinition()
    if base_def is None:
        return

    quoted = '\"%s\"' % base_def.type()
    def_list.append(quoted)
    return get_base_definitions(base_def, def_list)


def list_items(parent, level, options):
    ''' Lists items contained by parent

    The level input sets the indentation
    Conditional children are prefixed with "*"
    '''
    this_indent = options.indent * level
    if hasattr(parent, 'itemDefinition'):
        # Parent is Definition or GroupItemDefinition
        n = parent.numberOfItemDefinitions()
        for i in range(n):
            item = parent.itemDefinition(i)
            concrete_item = smtk.to_concrete(item)
            type_string = smtk.attribute.Item.type2String(item.type())
            print '%s%s \"%s\"' % (this_indent, type_string, item.name())
            list_items(concrete_item, level + 1, options)
    elif hasattr(parent, 'childrenItemDefinitions'):
        # Parent is ValueItemDefinition
        item_dict = parent.childrenItemDefinitions()
        item_list = item_dict.items()
        if item_list is None:
            return

        item_list.sort()
        for name, item in item_list:
            concrete_item = smtk.to_concrete(item)
            type_string = smtk.attribute.Item.type2String(item.type())
            print '%s*%s \"%s\"' % (this_indent, type_string, name)
            list_items(concrete_item, level + 1, options)


def list_definition(system, defn, level, options):
    '''Lists one definition in indented-text format

    '''
    this_indent = options.indent * level
    sublevel = level + 1
    base_list = list()
    get_base_definitions(defn, base_list)

    if base_list:
        base_string = ' : '.join(base_list)
        print '%sAttDef \"%s\" : %s' % (this_indent, defn.type(), base_string)
    else:
        print '%sAttDef \"%s\"' % (this_indent, defn.type())

    list_items(defn, sublevel, options)


def list_by_view(system, view, level, options):
    '''Lists definitions in indented-text format, organized by view

    Works recursively
    '''
    this_indent = options.indent * level
    sublevel = level + 1

    print '%sVIEW \"%s\"' % (this_indent, view.title())

    # Group view
    if hasattr(view, 'numberOfSubViews'):
        n = view.numberOfSubViews()
        for i in range(n):
            subview = view.subView(i)
            concrete_subview = smtk.to_concrete(subview)
            list_by_view(system, concrete_subview, sublevel, options)

    # Attribute view
    elif hasattr(view, 'numberOfDefinitions'):
        n = view.numberOfDefinitions()
        for i in range(n):
            defn = view.definition(i)
            list_definition(system, defn, sublevel, options)

    # Instanced view
    elif hasattr(view, 'numberOfInstances'):
        n = view.numberOfInstances()
        for i in range(n):
            att = view.instance(i)
            defn = att.definition()
            list_definition(system, defn, sublevel, options)


def list_definitions(system, options):
    '''Lists all definitions in indented-text format

    '''
    base_list = system.findBaseDefinitions()
    # print base_list

    def_list = list()
    for defn in base_list:
        derived_list = system.findAllDerivedDefinitions(defn, True)
        def_list.extend(derived_list)

    def_list.sort(key=lambda d: d.type())
    for defn in def_list:
        """
        base_list = list()
        get_base_definitions(defn, base_list)
        if base_list:
          base_string = ' : '.join(base_list)
          print '%s : %s' % (defn.type(), base_string)
        else:
          print defn.type()

        list_items(defn, 1, options)
        """
        list_definition(system, defn, 1, options)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description=app_description)
    parser.add_argument('-t', '--template_filename', required=True)
    parser.add_argument('-s', '--spaces_per_indent', type=int,
                        default=default_spaces_per_indent)
    parser.add_argument('-v', '--list_by_view', action='store_true')
    args = parser.parse_args()

    # Construct options object
    options = type('Options', (object,), {})
    options.indent = ' ' * args.spaces_per_indent
    options.list_by_view = args.list_by_view

    #  Load template file
    logger = smtk.util.Logger()
    print 'Loading template file %s' % args.template_filename
    system = smtk.attribute.Collection()
    reader = smtk.util.AttributeReader()
    err = reader.read(system, args.template_filename, logger)
    if err:
        print 'Abort: Could not load template file'
        print logger.convertToString()
        sys.exit(-2)

    # List the output
    if options.list_by_view:
        root_view = system.rootView()
        list_by_view(system, root_view, 0, options)
    else:
        list_definitions(system, options)
