#!/usr/bin/env python

__doc__ = """zendoc_base

Create an XML file showing the items in Zenoss.
This should be run on a fresh install of Zenoss with
no ZenPacks installed.
"""

import os
import os.path
import datetime

import Globals
from Products.ZenUtils.ZenScriptBase import ZenScriptBase
from Products.ZenUtils.Utils import importClass, zenPath
from transaction import commit

class XMLContainer:
    id = ''
    description = ''
    path = ''
    meta_type = ''

    def __init__(self, id='', meta_type=''):
        self.id = id
        self.meta_type = meta_type

    def getPrimaryPath(self):
        return self.path

threshie = XMLContainer('threshold', 'ThresholdContainer')
plugin_container = XMLContainer('modeler_plugin_container',
                                'ModelerPluginContainer')

class ModelerPlugin:
    id = ''
    description = ''
    path = ''
    meta_type = 'ModelerPlugin'

    def getPrimaryPath(self):
        return self.path.split('/')


def print_xml_tree(tree, indent=0):
    if '__id' in tree:
        header = " " * indent + """<%(__meta_type)s id="%(__id)s" location="%(__location)s" """ % tree
        if '__class' in tree and tree['__class']:
            header += 'class="%s"' % tree['__class']
        if '__hasTransform' in tree:
            header += ' hasTransform="%s"' % tree['__hasTransform']
        print header + " >"
        desc = tree['__description']
        if desc:
            print " " * indent + """<![CDATA[%s]]>""" % desc

    for obj in sorted(tree.keys()):
        if obj.startswith('__'): continue
        print_xml_tree(tree[obj], indent + 1 )

    if '__id' in tree:
        print " " * indent + "</%(__meta_type)s>" % tree

def add_object(root, obj):
    root['__id'] = obj.id
    root['__description'] = obj.description
    root['__location'] = '/'.join(obj.getPrimaryPath())
    root['__meta_type'] = obj.meta_type
    root['__class'] = obj.__class__.__name__
    if obj.__class__.__name__ == 'EventClass':
        if obj.transform:
            root['__hasTransform'] = 'yes'
        else:
            root['__hasTransform'] = 'no'

def get_modeler_description(path, file):
    location = path.replace(zenPath(), '').replace('/', '', 1).replace('/', '.')
    name = file.replace('.py', '')
    location += '.' + name
    try:
        mod = __import__(location, globals(), locals(), name)
        desc = mod.__doc__
    except:
        desc = 'Unable to load plugin %s' % location
    return desc

def add_plugin(root, file, path):
    plugin = ModelerPlugin()
    plugin.id = file.replace('.py', '')
    plugin.path = path
    plugin.description = get_modeler_description(path, file)
    add_object(root, plugin)

def modelerSearch(root, plugin_dir):
    if not os.path.exists(plugin_dir):
        return
    
    # Yes, you're right -- the following should be equivalent
    # root = root['']['zport']['dmd']
    #  But it's not.  Move along.
    for loc in root:  # Dereference ''
        root = root[loc]
    for loc in root:  # Dereference 'zport'
        root = root[loc]
    for loc in root:  # Dereference 'dmd'
        root = root[loc]
    # Back to sanity. Yay!
    madeOrganizer = False
    for path, unused, files in os.walk(plugin_dir):
        for file in files:
            if not file.endswith('.py') or file == '__init__.py':
                continue
            if not madeOrganizer:
                root['Modelers'] = {}
                new_root = root['Modelers']
                add_object(new_root, plugin_container)
                madeOrganizer = True
            new_root[file] = {}
            add_plugin(new_root[file], file, path)


def zenpack2XML(dmd, zp):
    dependencies = ', '.join( [' '.join([x,y]) for x, y in sorted(zp.dependencies.items()) ] )
    print "<zenpack id='%s' version='%s' author='%s' dependencies='%s' >" % (
          zp.id, zp.version, zp.author, dependencies)

    # Since the packables aren't arranged hierarchically,
    # we need to build our own tree.
    tree = {}
    for obj in zp.packables():
        root = tree
        obj_path = obj.getPrimaryPath()
        for i, loc in enumerate(obj_path):
            if loc not in root:
                root[loc] = {}
                # Graphs don't have their own node
                if i > 0 and  obj_path[i-1] == 'graphDefs':
                    graph_path = '/'.join(obj_path[:i+1])
                    graph_obj = dmd.getObjByPath(graph_path)
                    add_object(root[loc], graph_obj)

                # Thresholds don't have a container
                if loc == 'thresholds':
                    add_object(root[loc], threshie)

            root = root[loc]
        add_object(root, obj)
    modelerSearch(tree, zp)

    # Print out the tree
    print_xml_tree(tree)

    print "</zenpack>"

def describeDevClass(obj, tree):
    root = tree
    obj_path = obj.getPrimaryPath()
    for i, loc in enumerate(obj_path):
        if loc not in root:
            root[loc] = {}
            # Graphs don't have their own node
            if i > 0 and  obj_path[i-1] == 'graphDefs':
                graph_path = '/'.join(obj_path[:i+1])
                graph_obj = dmd.getObjByPath(graph_path)
                add_object(root[loc], graph_obj)

            # Thresholds don't have a container
            if loc == 'thresholds':
                add_object(root[loc], threshie)

        root = root[loc]
    add_object(root, obj)


def getDefaultModelers(dmdObj):
    defaultDirs = [
        zenPath('Products', 'DataCollector', 'plugins', 'zenoss'),
    ]
    tree = { '':{'zport':{'dmd':{}}}}
    for dir in defaultDirs:
        modelerSearch(tree, dir)
    print_xml_tree(tree)

def processDevClass(dmdObj):
    tree = {}
    describeDevClass(dmdObj, tree)
    for template in dmdObj.rrdTemplates.objectValues():
        describeDevClass(template, tree)
    print_xml_tree(tree)
    for obj in dmdObj.objectValues():
        if obj.meta_type == 'DeviceClass':
            processDevClass(obj)


def xml_header():
    export_date = datetime.datetime.now()
    print """<?xml version="1.0" encoding="UTF-8"?>

<!-- Base Zenoss information generated on %s -->
<zenbase>
""" % (export_date) 

def xml_trailer():
    print """
</zenbase>
"""


if __name__ == '__main__':
    xml_header()
    dmd = ZenScriptBase(connect=True).dmd
    getDefaultModelers(dmd.Devices)
    processDevClass(dmd.Devices)
    xml_trailer()
