# The Admin4 Project
# (c) 2013-2022 Andreas Pflug
#
# Licensed under the Apache License, 
# see LICENSE.TXT for conditions of usage


import version
import wx
import logger

app = wx.App()

import adm
import sys,os
import xmlres
import config
import getopt
import frame
from wh import StringType, SetLoaddir

logger.loglevel=logger.LOGLEVEL.DEBUG
IGNORE_MODULES=['xrced', 'dist', 'build', 'release', 'lib', 'include', 'mpl-data', 'tcl']

def getRevision(revStr, modRevision, modDate):
  s=revStr.split()
  if len(s) > 5:
    revision=int(s[2])
    if revision > modRevision:
      modRevision=revision
      modDate=s[3]
  return modRevision, modDate

    

def loadModules(modlist=None):
  adm.mainRevision=0
  adm.mainDate=version.revDate
  
  ignorePaths = [ os.path.abspath(fn) for fn in [sys.argv[0], __file__]]
  names=os.listdir(adm.loaddir)

  for modid in names:
    if modid.startswith('.'):
      continue
    path=os.path.join(adm.loaddir, modid)

    if path in ignorePaths:
      continue
    loadModule(modid, path, modlist)


def loadModule(modid, path, modlist=None):
    if os.path.isdir(path):
      if modid.startswith('_'):
        return
      if modid in IGNORE_MODULES:
        return
      if not os.path.exists(os.path.join(path, "__init__.py")) and not os.path.exists(os.path.join(path, "__init__.pyc")):
        logger.debug("No python module: %s", modid)
        return
      adm.availableModules.append(modid)
      if modlist and modid not in modlist:
        logger.debug("Module %s ignored", modid)
        return

      mod=__import__(modid)
      if not mod:
        return
      try:
        __import__("%s._requires" % modid)
        requires=getattr(mod, "_requires")
        if not requires.GetPrerequisites():
          logger.debug("Module %s Prerequisites not met", modid)
          return
      except:
        pass
      
      modname=mod.moduleinfo['name']
      adm.modules[modid] = mod
      mod.moduleinfo['class'] = modid
      if not mod.moduleinfo.get('serverclass'):
        if not hasattr(mod, "Server"):
          logger.debug("Module %s has no Server import" % modid)
          return
        if not hasattr(mod.Server, "Server"):
          logger.debug("Module %s.Server has no Server class" % modid)
          return
        mod.moduleinfo['serverclass'] = mod.Server.Server

      logger.debug("Loading Module %s '%s'", modid, modname)
      if hasattr(mod, "Preferences"):
        mod.moduleinfo['preferences'] = mod.Preferences

      modRevision=mod.moduleinfo.get('revision', 0)
      modDate=None

      if hasattr(mod, "revision"):
        modRevision, modDate = getRevision(mod.revision, modRevision, modDate)

      nodelist={}
      menulist=[]

      for nodefile in os.listdir(path):
        if nodefile.startswith("_") or not nodefile.endswith(".py"):
          continue
        nodemodname=nodefile[:-3]
        nodemod="%s.%s" % (modid, nodemodname)

        try:
          node=getattr(mod, nodemodname)
        except:
          try:
            __import__(nodemod)
            node=getattr(mod, nodemodname)
          except Exception as e:
            logger.debug("No Module member: %s - %s", nodemodname, e)
            continue

        # get menuinfo
        if hasattr(node, "menuinfo"):
          menulist.extend(node.menuinfo)
        if hasattr(node, "pageinfo"):
          pages=mod.moduleinfo.get('pages')
          if pages:
            pages.extend(node.pageinfo)
          else:
            mod.moduleinfo['pages'] = node.pageinfo

        # check revisions
        if hasattr(node, "revision"):
          modRevision, modDate = getRevision(node.revision, modRevision, modDate)


        if not hasattr(node, "nodeinfo"):
          logger.debug("No nodeinfo in %s", nodefile)
          continue
        nodeinfo=node.nodeinfo

        # get nodeinfo
        if not isinstance(nodeinfo, list):
          nodeinfo=[nodeinfo]
        for ni in nodeinfo:
          nodename=ni['class'].__name__
          pages=ni.get('pages')
          if pages:
            if isinstance(pages, StringType):
              ni['pages']=pages.split(' ')
            elif isinstance(pages, list):
              l=[]
              for p in pages:
                if isinstance(p, StringType):
                  l.append(p)
                else:
                  l.append(p.__name__)
              ni['pages']=l
            else:
              ni['pages'] = [pages.__name__]
          nodelist[nodename]=ni
          ni['children']=[]
          logger.debug("Loading node %s.%s", modid, nodename)

      for nodename, nodeinfo in nodelist.items():
        childlist=[]
        # assign childs to nodeinfo
        for node, ni in nodelist.items():
          parents=ni.get('parents')
          if not isinstance(parents, list):
            parents=[parents]
          for p in parents:
            if p and not isinstance(p, StringType):
              p=p.__name__
            if p == nodename:
              s=ni.get('sort')
              if s:
                child="%s:%s" % (str(s), ni['class'].__name__)
              else:
                child=ni['class'].__name__
              childlist.append(child)

        childlist.sort()
        for child in childlist:
          cn=child[child.find(':')+1:]
          nodeinfo['children'].append(cn)

      mod.moduleinfo['nodes']=nodelist

      # sort and remember module menuinfo
      menus=[]
      for mi in menulist:
        cls=mi['class']
        if hasattr(cls, "OnExecute"):
          cls.OnExecute._classname_=cls.__name__
        else:
          logger.debug("Menu %s has no OnExecute", str(cls))
          continue
        nodeclasses=mi.get('nodeclasses')
        if nodeclasses:
          if isinstance(nodeclasses, str):
            nodeclasses=nodeclasses.split(' ')
          elif not isinstance(nodeclasses, list):
            nodeclasses=[nodeclasses]
          ncs=[]
          for nc in nodeclasses:
            if isinstance(nc, StringType):
              nl=nodelist.get(nc)
              if nl:
                nc=nl['class']
              else:
                logger.debug("Menu %s references unknown class %s", str(mi['class']), nc)
            ncs.append(nc)
          mi['nodeclasses'] = ncs
        menus.append(mi)

      mod.moduleinfo['menus']=sorted(menus, key=lambda mi: mi.get('sort'))
      tools=[]
      for mi in mod.moduleinfo['menus']:
        if hasattr(mi['class'], 'toolbitmap'):
          tools.append(mi)
      mod.moduleinfo['tools']=tools
      
      # sort pages
      pages=mod.moduleinfo.get('pages')
      if pages:
        mod.moduleinfo['pages'] = sorted(pages, key=lambda pageClass: pageClass.order)
        for page in mod.moduleinfo['pages']:
          if hasattr(page, 'availableOn'):
            classes=page.availableOn
            if isinstance(classes, StringType):
              classes=classes.split(' ')
            for cls in classes:
              ni=mod.moduleinfo['nodes'].get(cls)
              nodePages=ni.get('pages')
              if nodePages:
                if not cls in nodePages:
                  nodePages.append(page.__name__)
              else:
                ni['pages'] = [page.__name__]
      mod.moduleinfo['revision']=modRevision
      mod.moduleinfo['date']=modDate
      logger.debug("Module %s revision %s, date %s", modid, modRevision, modDate)


    elif path.endswith((".py", '.pyc')):
      if modid.startswith('__'):
        return
      mod=__import__(modid[:modid.rfind('.')])
      if hasattr(mod, "revision"):
        adm.mainRevision, adm.mainDate=getRevision(mod.revision, adm.mainRevision, adm.mainDate)

  
def LoggerExceptionHook(exType, args, tb):
  sys.__excepthook__(exType, args, tb)
  logger.sysexception(exType, args, tb)


def main(argv):
  adm.app = app
  adm.loaddir=os.path.dirname(os.path.abspath(argv[0]))
  SetLoaddir(adm.loaddir)


  _dn, an=os.path.split(argv[0])
  dot=an.rfind('.')
  if dot > 0:
    adm.appname=an[0:dot]
  else:
    adm.appname=an

  sys.excepthook=LoggerExceptionHook
  modules=[]

  if sys.platform == "darwin":    macOpt="p"
  else:                           macOpt=""
  opts, args = getopt.getopt(argv[1:], "m:n:%s" % macOpt, ["modules=", "name="])
  for opt in opts:
    if opt[0] in ['-m', '--modules']:
      modules = map(lambda x: "mod%s" % x.capitalize(), opt[1].split(','))
    elif opt[0] in ['-n', '--name']:
      adm.appname=opt[1]
    elif opt[0] == '-p':
      pass
  
  app.SetAppName(adm.appname)
  adm.config=config.Config(adm.appname)
  frame.LoggingDialog.Init()

  adm.appTitle=adm.config.Read("Title", adm.appname.title())
  if wx.VERSION > (2,9):
    app.SetAppDisplayName(adm.appTitle)
    from version import vendor, vendorDisplay
    app.SetVendorName(vendor)
    app.SetVendorDisplayName(vendorDisplay)

  
#  if not modules:
#    modules=adm.config.Read("Modules", [])
  if not modules:
    dot=adm.appname.find('-')
    if dot>0:
      modules=["mod" + adm.appname[:dot].capitalize()]

  loadModules(modules)
  xmlres.init(adm.loaddir)

  adm.mainframe=frame.DetailFrame(None, adm.appTitle, args)
  app.SetTopWindow(adm.mainframe)

  for panelclass in adm.getAllPreferencePanelClasses():
    if hasattr(panelclass, "Init"):
      panelclass.Init()

  adm.mainframe.Show()

  app.MainLoop()
