
import Options
import Arguments
import CommandLineUtil
import FancyGetOpt

from xml.dom.html import htmlImplementation
from xml.dom.ext import PrettyPrint
import cStringIO, string


class Command:
    def __init__(self,
                 name,
                 description,
                 example,
                 verbose_description,
                 function = None,
                 options=None,
                 arguments = None,
                 subCommands = None,
                 authenticate = 0,
                 heading = None
                 ):

        self.name = name
        self.description = description
        self.function = function
        self.example = example
        self.verbose_description = verbose_description
        self.options = options or Options.Options()
        self.arguments = arguments or []
        self.subCommands = subCommands or {}
        self.authenticate = authenticate
        self.heading = heading

        if type(self.subCommands) == type([]):
            cmds = {}
            for c in self.subCommands:
                cmds[c.name] = c
            self.subCommands = cmds


        #Validate Options
        if not isinstance(self.options,Options.Options):
            raise Exception("Options not an instance of Options.Options")

        for a in self.arguments:
            if not isinstance(a,Arguments.Argument):
                raise Exception("Argument is not an Argument: %s" % str(a))

    def build_parent_relationship(self):
        for c in self.subCommands.values():
            c.parent = self
            c.build_parent_relationship()

    def break_parent_relationship(self):
        for c in self.subCommands.values():
            if hasattr(c,'parent'):
                delattr(c,'parent')
            c.break_parent_relationship()


    def flatten_command_tree(self,level,previousName=''):
        if previousName:
            fName = previousName + '.' + self.name
        else:
            fName = self.name

        res = [(level,self,fName)]
        names = self.subCommands.keys()
        names.sort()
        for name in names:
            res.extend(self.subCommands[name].flatten_command_tree(level+1,previousName = fName))
        return res


    def run_command(self,auth):
        if self.authenticate:
            self.clOptions['AUTHENTICATION'] = auth(options=self.clOptions,level=self.authenticate)
        if self.heading:
            print self.heading
        self.function(self.clOptions, self.clArguments)
        return


    def validate_arguments(self,args):
        eatenArgs = {}
        uneatenArgs = filter(string.strip, args)
        for arg in self.arguments:
            eaten,uneatenArgs = arg.validate(self,uneatenArgs)
            if eaten is not None:
                eatenArgs[arg.name] = eaten

        if len(uneatenArgs):
            raise CommandLineUtil.ArgumentError(self,"Unknown Argument %s" % uneatenArgs[0])
        return eatenArgs

    def validate_options(self,options):
        if options.get('help'):
            raise CommandLineUtil.ArgumentError(self,'')
        for opt in self.options:
            opt.apply_options(options)
        for opt in self.options:
            opt.validate()
        return 1



    def _gen_usage(self,command_string):
        lines = []

        # saved for use in the example
        command_size = len(command_string)
            
        lines = self.__gen_command_line_help(command_string)

        # Display the example, if available
        if self.example is not None:
            lines.append('\nExample:')
            text_width = 78 - command_size
            indent = ' ' * command_size

            text = CommandLineUtil.wrap_text(self.example, text_width)

            lines.append('%s%s' % (command_string, text[0]))
            for line in text[1:]:
                lines.append('%s%s' % (indent, line))

        # Now do the description of the options
        option_desc = self.options.generate_help()
        if option_desc:
            lines.append('\nOptions:')
            lines.extend(option_desc)

        if self.subCommands:
            max_cmd = 0
            for cmd in self.subCommands.keys():
                if len(cmd) > max_cmd:
                    max_cmd = len(cmd)
            
            lines.append('\nSubcommands:')
            # column size = indent + longest command + gutter
            indent = ' ' * (2 + max_cmd + 2) + '  '
            text_width = 78 - len(indent)
            names = self.subCommands.keys()
            names.sort()
            for name in names:
                cmd = self.subCommands[name]
                text = CommandLineUtil.wrap_text(cmd.description, text_width)
                lines.append('  %-*s  %s' % (max_cmd, name, text[0]))
                for line in text[1:]:
                    lines.append(indent + line)

        #Argument desc:
        if self.arguments:
            max_arg = 0
            for arg in self.arguments:
                if len(arg.name) > max_arg:
                    max_arg = len(arg.name)
            
            lines.append('\nArguments:')
            # column size = indent + longest command + gutter
            indent = ' ' * (2 + max_arg + 2)
            text_width = 78 - len(indent)
            for arg in self.arguments:
                text = CommandLineUtil.wrap_text(arg.description, text_width)
                lines.append('  %-*s  %s' % (max_arg, arg.name, text[0]))
                for line in text[1:]:
                    lines.append(indent + line)
                
        lines.append('')    # add a blank line
        lines.extend(CommandLineUtil.wrap_text(self.verbose_description, 78))

        lines.append('')    # add a blank line
        return lines



    def _parse_command_opts(self, args):
        (options, args) = FancyGetOpt.FancyGetopt(self,self.options,
                                                  args)


        if not self.validate_options(options):return None
        self.clOptions = options

        #Args can eiter be a sub command, or the start of the argument list
        #sub command takes precedence
        if len(args) and self.subCommands.has_key(args[0]):
            #It is a sub command
            cmd = self.subCommands[args[0]]
            finalCmd = cmd._parse_command_opts(args[1:],)
        else:
            #The are arguments
            finalCmd = self
            self.clArguments = self.validate_arguments(args)
            
        return finalCmd


    def __gen_command_line_help(self,command_string):

        lines = []
        # saved for use in the example
        command_size = len(command_string)

        command_line = ''
        for opt in self.options:
            command_line = command_line + opt.gen_command_line() + ' '

        #either sub commands or args
        if self.subCommands:
            if len(self.arguments):
                command_line = command_line + '['
            command_line = command_line + '<sub-command> '
            if len(self.arguments):
                command_line = command_line + '['
            command_line = command_line + '<sub-command> '
            if len(self.arguments):
                command_line = command_line + '] | ['

        if self.arguments:
            for arg in self.arguments:
                command_line = command_line + arg.gen_command_line() + ' '
            if self.subCommands:
                command_line = command_line + ']'

        # wrap the command line to 78 characters
        # indented the width of the script name and commands
        text = CommandLineUtil.wrap_text(command_line, 78 - command_size)
        lines.append(command_string + text[0])
        indent = ' ' * command_size
        for line in text[1:]:
            lines.append(indent + line)

        return lines

    def generate_html_help(self,file,fullName):

        sFullName = string.join(string.split(fullName,'.'),' ')

        doc = htmlImplementation.createHTMLDocument("%s command help" % sFullName)

        body = doc.body
        self._generate_html_help(body,fullName)
        st = open(file,'w')
        PrettyPrint(doc,st)


    def _generate_html_help(self,parent,fullName):

        doc = parent.ownerDocument
        sFullName = string.join(string.split(fullName,'.'),' ')

        h1 = doc.createElement('H1')
        h1.appendChild(doc.createTextNode(sFullName))
        parent.appendChild(h1)
        

        bq = doc.createElement('BLOCKQUOTE')
        bq.appendChild(doc.createTextNode(self.verbose_description))
        parent.appendChild(bq)

        lines = self.__gen_command_line_help(sFullName)
        pre = doc.createElement('PRE')
        for l in lines:
            pre.appendChild(doc.createTextNode(l+'\n'))
        parent.appendChild(pre)

        #Show an example
        if self.example is not None:
            h2 = doc.createElement('H2')
            h2.appendChild(doc.createTextNode('Example'))
            parent.appendChild(h2)

            bq = doc.createElement('BLOCKQUOTE')
            bq.appendChild(doc.createTextNode("%s %s"%(sFullName,self.example)))
            parent.appendChild(bq)
        

        #Generate the options help
        opTable = doc.createElement('TABLE')
        opTable.border='1'
        opTable.cellSpacing = '0'
        opTable.width = '600'
        opTable.cellPadding = '3'
        
        tr = doc.createElement('TR')
        td = doc.createElement("TD")
        td.bgColor = '#FF666'
        td.colSpan = '2'
        big = doc.createElement('BIG')
        bold = doc.createElement('B')
        bold.appendChild(doc.createTextNode("Option Summary"))
        big.appendChild(bold)
        td.appendChild(big)
        tr.appendChild(td)
        opTable.appendChild(tr)

        # Now do the description of the options
        self.options.generate_html_help(opTable)
        parent.appendChild(opTable)
 
        if self.subCommands:

            parent.appendChild(doc.createElement('BR'))
            subTable = doc.createElement('TABLE')
            subTable.border='1'
            subTable.cellSpacing = '0'
            subTable.width = '600'
            subTable.cellPadding = '3'


            tr = doc.createElement('TR')
            td = doc.createElement("TD")
            td.bgColor = '#6666FF'
            td.colSpan = '2'
            big = doc.createElement('BIG')
            bold = doc.createElement('B')
            bold.appendChild(doc.createTextNode("Sub Command Summary"))
            big.appendChild(bold)
            td.appendChild(big)
            tr.appendChild(td)
            subTable.appendChild(tr)

            
            names = self.subCommands.keys()
            names.sort()
            for name in names:
                sub = self.subCommands[name]
                tr = doc.createElement('TR')
                td = doc.createElement('TD')
                a = doc.createElement('A')
                a.href = fullName + '.' + sub.name + '-command-line.html'
                a.appendChild(doc.createTextNode(sub.name))
                td.appendChild(a)
                tr.appendChild(td)
                td = doc.createElement('TD')
                td.appendChild(doc.createTextNode(sub.description))
                tr.appendChild(td)
                subTable.appendChild(tr)
            parent.appendChild(subTable)


        if self.arguments:
            parent.appendChild(doc.createElement('BR'))
            argTable = doc.createElement('TABLE')
            argTable.border='1'
            argTable.cellSpacing = '0'
            argTable.width = '600'
            argTable.cellPadding = '3'


            tr = doc.createElement('TR')
            td = doc.createElement("TD")
            td.bgColor = '#66FF66'
            td.colSpan = '2'
            big = doc.createElement('BIG')
            bold = doc.createElement('B')
            bold.appendChild(doc.createTextNode("Argument Summary"))
            big.appendChild(bold)
            td.appendChild(big)
            tr.appendChild(td)
            argTable.appendChild(tr)

            
            for arg in self.arguments:
                tr = doc.createElement('TR')
                td = doc.createElement('TD')
                td.appendChild(doc.createTextNode(arg.name))
                tr.appendChild(td)
                td = doc.createElement('TD')
                td.appendChild(doc.createTextNode(arg.description))
                tr.appendChild(td)
                argTable.appendChild(tr)


            parent.appendChild(argTable)





