#
# Time for an initial checkin.  Datastructure and general layout of the
# code is correct.  Still need comparison code for File and RPM objects.
#

import os
import sys
import types
import sunhpc
import getopt
import sunhpc.invoke
import xml
from xml.sax import saxutils
from xml.sax import handler
from xml.sax import make_parser
from xml.sax._exceptions import SAXParseException

class Application:

    def __init__(self, argv=None):

        # Applets (code in the kickstart graph) doesn't pass sys.argv
        # so pick it up anyway to keep everything working.
        
        if not argv:
            argv = sys.argv
            
        self.args		= []
        self.caller_args	= argv[1:]
        self.usage_command	= os.path.basename(argv[0])
        self.usage_name		= 'Application'
        self.usage_version	= 0
        self.rcfileHandler      = RCFileHandler
        self.rcfileList		= []
        self.rcForce		= []

        self.projectName	 = 'sunhpc'
        self.projectVersionName  = 'base'
        self.projectVersionMajor = sunhpc.version_major
        self.projectVersionMinor = sunhpc.version_minor
        self.projectVersionMicro = sunhpc.version_micro
        

        self.getopt		= sunhpc.core.utils.Struct()
        self.getopt.s		= [ 'h' ]
        self.getopt.l		= [ 'help',
                                    'list-rcfiles',
                                    'list-project-info',
				    'rcfile='
				    ]

        # Unset our local
        try:
            os.environ["LANG"]
        except KeyError:
            pass

    def run(self):
        sys.exit(-1)

    def projectInfo(self):
        return [ self.projectName,
                 self.projectVersionName,
                 int(self.projectVersionMajor),
                 int(self.projectVersionMinor),
                 int(self.projectVersionMicro) ]

    def getArgs(self):
        return self.args

    def setArgs(self, argslist):
        self.args = argslist


    def parseArgs(self, rcbase=None):
        """Parses the command line arguments and all the relevant
        resource-control (RC) files for this application. The usage_command
        (generally argv[0]) will determine which the name of our rcfile,
        unless overrided with the rcbase argument."""

        # Save any existing options
        args = self.getArgs()

        # First pass to get rcfiles specified on the cmd line
        self.setArgs(self.caller_args)
        self.parseCommandLine(rcfile=1)

        # Parse Resource Control files
        self.setArgs([])
        if not rcbase:
            rcbase = self.usage_command
        self.parseRC(rcbase)
        
        for rc in self.rcForce:
            self.parseRCFile(rc, rcbase)

        # Command line options always win
        self.setArgs(args + self.args + self.caller_args)
        self.parseCommandLine()


    def parseRC(self, rcbase, section=None):
        rcfile  = rcbase + 'rc'

        if not section:
            section = rcbase

        # Where we look for resource-control files. First in 
        # the default 'etc' location, then HOME, finally in this dir.
        # We use data from all three, such that later rc files take 
        # precedence.

        dirList = [ os.path.join(os.sep,'opt', self.projectName, 'etc') ]
        if 'HOME' in os.environ:
            dirList.append(os.environ['HOME'])
        dirList.append('.')

        # Look for both hidden and visible rc files.
        for dir in dirList:
            self.parseRCFile(os.path.join(dir, '.' + rcfile), section)
            self.parseRCFile(os.path.join(dir, rcfile), section)

    def parseRCFile(self, filename, section):
        if os.path.isfile(filename) and filename not in self.rcfileList:
            self.rcfileList.append(filename)
            file = open(filename, 'r')
            parser  = make_parser()
            handler = self.rcfileHandler(self)
            if section:
                handler.setSection(section)
            parser.setContentHandler(handler)
            try:
                parser.parse(file)
            except SAXParseException as msg:
                print (filename, "XML parse exception: ", msg)
            file.close()
            
    def parseCommandLine(self, rcfile=0):
        """Calls getopt to parse the command line flags. In
        rcfile mode we just get --rcfile options."""

        short = ''
        for e in self.getopt.s:
            if isinstance(e, tuple):
                short = short + e[0]
            else:
                short = short + e

        long = []
        for e in self.getopt.l:
            if isinstance(e, tuple):
                long.append(e[0])
            else:
                long.append(e)

        try:
            opts, args = getopt.getopt(self.args, short, long)
        except getopt.GetoptError as msg:
            sys.stderr.write("error - %s\n" % msg)
            self.usage()
            sys.exit(1)

        for c in opts:
            if rcfile:
                if c[0] != "--rcfile":
                    continue
            self.parseArg(c)

        if not rcfile:
            self.args = args

    def parseArg(self, c):
        if c[0] in ('-h', '--help'):
            self.help()
            sys.exit(0)
        elif c[0] == '--list-rcfiles':
            print (self.rcfileList)
            sys.exit(0)
        elif c[0] == '--list-project-info':
            print (self.projectInfo())
            sys.exit(0)
        elif c[0] == '--rcfile':
            self.rcForce.append(c[1])
        else:
            return 0
        return 1
    
    def usage(self):

        if 'COLUMNS' in os.environ:
            cols = os.environ['COLUMNS']
        else:
            cols = 80

        usagelist = [ 'Usage: ', self.usage_command, ' ' ]
        
        # Build string of argument-free short options.
        s = '[-'
        for e in self.getopt.s:
            if len(e) == 1:
                s = s + e
        s = s + ']'
        if len(s) == 3:
            s = ''
        usagelist.append(s)

        # Add the argument short options to the above string
        for e in self.getopt.s:
            if isinstance(e, tuple):
                v = e[0]
                h = e[1]
            else:
                v = e
                h = 'arg'
            if len(v) != 1:
                usagelist.append(' [-' + v[:-1] + ' ' + h + ']')

        # Add argument-free long options
        for e in self.getopt.l:
            if isinstance(e, tuple):
                v = e[0]
            else:
                v = e
            if v[len(v)-1] != '=':
                usagelist.append(' [--' + v + ']')

        # Add argument long options
        for e in self.getopt.l:
            if isinstance(e, tuple):
                v = e[0]
                h = e[1]
            else:
                v = e
                h = 'arg'
            if v[len(v)-1] == '=':
                usagelist.append(' [--' + v[:-1] + ' ' + h + ']')

        usagelist.append(self.usageTail())

        # Print the usage, word wrapped to the correct screen size.
        print (self.usage_name, '- version', self.usage_version)
        l = 0
        s = ''
        for e in usagelist:
            if l + len(e) <= cols:
                s = s + e
                l = l + len(e)
            else:
                print (s)
                l = len(e)
                s = e
        if s:
            print (s)

    def help(self):
        self.usage()

    def usageTail(self):
        return ''

    def getArch(self):
        return sunhpc.core.utils.getNativeArch()

class RCFileHandler(sunhpc.core.utils.ParseXML):

    def __init__(self, application):
        sunhpc.core.utils.ParseXML.__init__(self, application)
        self.foundSection = 0
        self.section	  = self.app.usage_command

    def setSection(self, section):
        self.section = section

    def startElement(self, name, attrs):
        #
        # need to convert all the attributes to ascii strings.
        # starting in python 2, the xml parser puts the attributes in
        # unicode which causes problems with sunhpc apps classes, specifically
        # those that append path names found in the attributes to the sys.path
        #
        newattrs = {}
        for (aname, avalue) in attrs.items():
            newattrs[aname] = str(attrs[aname])

        if self.foundSection:
            sunhpc.core.utils.ParseXML.startElement(self, name, newattrs)
        elif name == self.section:
            self.startElementSection(name, newattrs)

    def endElement(self, name):
        if self.foundSection and name == self.foundSection:
            self.endElementSection(name)
        if self.foundSection:
            sunhpc.core.utils.ParseXML.endElement(self, name)

    def getOptions(self):
        return self.options


    # <section parent="base">

    def startElementSection(self, name, attrs):
        parent = attrs.get('parent')
        if parent:
            self.app.parseRC(parent, parent)
        self.foundSection = 1

    def endElementSection(self, name, attrs):
        self.foundSection = 0
    
    # <usage>

    def startElement_usage(self, name, attrs):
        usageName    = attrs.get('name')
        usageVersion = attrs.get('version')

        if usageName:
            self.app.usage_name = usageName
        if usageVersion:
            self.app.usage_version = usageVersion
        
    # <option>

    def startElement_option(self, name, attrs):
        argName  = attrs.get('name')
        # Will return None if value is not present.
        argValue = attrs.get('value')

        list = self.app.getArgs()
        
        if len(argName) > 1:
            flag = '--' + argName

            # We differentiate between empty values and
            # no value at all.
            if argValue is not None:
                list.append('%s=%s' % (flag, argValue))
            else:
                list.append(flag)
        else:
            flag = '-' + argName
            if argValue:
                list.append('%s %s' % (flag, argValue))
            else:
                list.append(flag)
        
        self.app.setArgs(list)


    # <project>

    def startElement_project(self, name, attrs):
        self.app.projectName = attrs.get('name')
        self.app.projectVersionName = attrs.get('versionName')
        self.app.projectVersionMajor = attrs.get('versionMajor')
        self.app.projectVersionMinor = attrs.get('versionMinor')
        self.app.projectVersionMicro = attrs.get('versionMicro')

