#!/usr/bin/env python

import os
import sys
import re
from select import select
import datetime
import time
import tempfile
import md5

from django.core.management import setup_environ
import settings

setup_environ(settings)

from mib.models import *

def fenoid(oid, module):
    if re.match(r'0\.[1-9]', oid):
	return oid.replace('0', mkoid(module))
    return oid

def mkoid(module):
    import struct
    return "1.3.6.1.4.1.6316.42.%d" % (struct.unpack('=i', md5.new(module).digest()[0:4])[0] & 0x7fffffff)

def smitype(syntax):
    ret = ''
    if syntax.has_key('module') and syntax.has_key('name'):
	if syntax['module']:
	    ret = syntax['module'] + '::'
	return ret + syntax['name']
    if syntax.has_key('parent module'):
	ret = "%(name)s::%(type)s" % syntax['parent module']
    elif syntax.has_key('basetype'):
	ret = syntax['basetype']
    else:
	ret = ",".join(syntax.keys())
    if syntax.has_key('range'):
	# TODO: ranges
	return "%s(%s..%s)" % (ret, syntax['range']['min'], syntax['range']['max'])
    else:
	# if it's enum or bits, look through all the keys.
	# some of them are dicts with 'nodetype': 'namednumber'
	# (others?)
	return "%s(subtyped somehow)" % ret

def smisyntax(type):
    if type.has_key('syntax'):
	return smitype(type['syntax']['type'])
    return ''

def process(file):
    source = file
    xdate = None
    try:
	f = open(file, "r")
    except IOError, e:
	print >> sys.stderr, e
	return
    first = f.readline().rstrip("\n")
    r = re.match(r'-- extracted from (.*)', first)
    if r:
	source = re.sub(r'\.txt$', '', r.group(1))
	second = f.readline().rstrip("\n")
	r = re.match(r'-- at (.*)', second)
	if r:
	    xdate = datetime.datetime(*(time.strptime(r.group(1), '%a %b %d %H:%M:%S %Y')[0:6]))
    # todo: read to end of file looking for copyright
    f.close()
# popen3 seems buggy, too bad.
#    (inf, out, err) = os.popen3(('smidump', '-f', 'python', file), bufsize=1048576)	# -k?
#    inf.close()
#    code = ""
#    errmsgs = ""
#    outeof = False
#    erreof = False
#    while not (outeof and erreof):
#	selectlist = []
#	if not(outeof):
#	    selectlist.append(out)
#	if not(erreof):
#	    selectlist.append(err)
#	print selectlist
#	ready = select([], selectlist, selectlist, 1)
#	print ready
#	for ready_fd in ready[1]:
#	    buf = ready_fd.read(2048)
#	    if buf != "":
#		if ready_fd == out:
#		    code += buf
#		else:
#		    errmsgs += buf
#	    else:
#		if ready_fd == out:
#		    outeof = True
#		else:
#		    erreof = True
#    inf.close()
#    out.close()
#    err.close()
    outfile = tempfile.mkstemp(prefix='smilint-out')
    errfile = tempfile.mkstemp(prefix='smilint-err')
    print file
    os.system('smidump -k -f python %s > %s 2> %s' % (file, outfile[1], errfile[1]))
    errmsgs = open(errfile[1], "r").read()
    os.unlink(errfile[1])
    code = open(outfile[1], "r").read()
    os.unlink(outfile[1])
    try:
	exec(code)
    except Exception, e:
	print >> sys.stderr, "%s - code generated by smidump has errors, %s" % (file, e)
	return

    try:
	module = MIB['moduleName']
    except NameError:
	print >> sys.stderr, "%s was too badly formed to have output\n%s" % (file, errmsgs)
	return
    print "Module: %s" % module
    modinfo = MIB[module]
    try:
	modrow = Module.objects.get(module=module)
    except Module.DoesNotExist:
	modrow = Module(module=module)
    modrow.source = source
    modrow.xdate = xdate
    modrow.contact = modinfo.get('contact', '')
    if modinfo.has_key('revisions'):
	modrow.lastrevised = modinfo['revisions'][0]['date']	# strptime?
    modrow.smidump = errmsgs
    modrow.org = modinfo.get('organization', '')
    modrow.save()

    mod_identity = modinfo.get('identity node', None)

    typeflag = False
    module_has_oid = False
    for node, nodeinfo in MIB.get('nodes', {}).items() + [(None, None)] + MIB.get('typedefs', {}).items():
	if node is None:
	    typeflag = True
	    continue
	try:
	    object = Object.objects.get(module=modrow, object=node)
	except Object.DoesNotExist:
	    object = Object(module=modrow, object=node)
	if typeflag:
	    object.type = 'tc'
	    object.syntax = smitype(nodeinfo)
	else:
	    object.type = nodeinfo['nodetype']
	    object.syntax = smisyntax(nodeinfo)
	object.access = nodeinfo.get('access', '')
	object.units = nodeinfo.get('UNITS', '')	# XXX case?
	object.displayhint = nodeinfo.get('DISPLAY-HINT', '')
	object.status = nodeinfo.get('status', 'unknown')
	if typeflag:
	    oid = ''
	else:
	    oid = fenoid(nodeinfo['oid'], module)
	object.oid = oid
	object.description = nodeinfo.get('description', '')
	object.reference = nodeinfo.get('reference', '')
	object.defval = nodeinfo.get('default', '')
	if node == mod_identity:
	    module_has_oid = True
	object.save()

    # Special handling for the identity node.
    if mod_identity is not None:
	try:
	    object = Object.objects.get(module=modrow, object=mod_identity)
	except Object.DoesNotExist:
	    object = Object(module=modrow, object=mod_identity)
	object.type = 'module'
	if modinfo.has_key('description'):
	    object.description = modinfo['description']
	if not module_has_oid:
	    object.oid = mkoid(module)
	object.save()

    for impinfo in MIB.get('imports', []):
	try:
	    fmodule = Module.objects.get(module=impinfo['module'])
	except Module.DoesNotExist:
	    fmodule = None
	    fobject = None
	if fmodule is not None:
	    try:
		fobject = Object.objects.get(module=fmodule, object=impinfo['name'])
	    except Object.DoesNotExist:
		fmodule = None
		fobject = None
	name = impinfo['module'] + '::' + impinfo['name']
	impobject, created = Import.objects.get_or_create(module=modrow, imp=name)
	impobject.srcmod = fmodule
	impobject.object = fobject
	impobject.save()

    # resolve unresolved imports
    for unresolved in Import.objects.filter(imp__startswith=module + '::', srcmod__isnull=True):
	objname = unresolved.imp.replace(module + '::', '')
	try:
	    object = Object.objects.get(module=modrow, object=objname)
	except Object.DoesNotExist:
	    continue
	unresolved.object = object
	unresolved.srcmod = modrow
	unresolved.save()

for file in sys.argv[1:]:
    # APPLETALK-MIB causes smidump to loop
    # SERVICE-LOCATION-PROTOCOL-MIB causes weird mysql error
    if file in ('Modem-MIB', 'SERVICE-LOCATION-PROTOCOL-MIB', \
		'APPLETALK-MIB', ):	# XXX
	continue
    if file.startswith('smilint.core'):
	continue
    process(file)
