import re

_imports = []
_parents = []
_version = 1

#Parse type definitions.
def _Parse_rawtext(avatar, userText, args):
  return (True, "", userText)

numRe = re.compile("^[0-9]+")
def _Parse_num(avatar, userText, args):
  matchObj = numRe.match(userText)
  if matchObj != None:
    return (True, userText[matchObj.end():], int(userText[:matchObj.end()]))
  else:
    return (False, None, None)


parseTypes = {"rawtext":_Parse_rawtext, "num":_Parse_num}

welcome = "\n\r\n\rWelcome!  Current commands are QUIT, SAY, and SHUTDOWN.\n\r"
COMMANDMODE_WORD, COMMANDMODE_PREFIX, COMMANDMODE_REGEXP = range(3)
#[priority, word or prefix or regexp, textMatch, commandMode, caseSensitive, commandFunc, permissions]


def EmptyParser(avatar, message):
  pass

def TakeControl(avatar):
  Send(avatar, welcome)
  avatar.handler = CommandHandler

def HasPermissions(account, permissions):
    for permission in permissions.keys():
      if not(account.permissions.has_key(permission)):
        return False
      elif account.permissions[permission] < permissions[permission]:
        return False
    return True
    
def FixCase(message, caseSensitive):
  if caseSensitive is False:
    return message.lower()
  else:
    return message

def CommandHandler(avatar, message):
  if message == "":
    return None
  commands = GenerateCommands(avatar)
  #List of commands in format (command, command trigger, text of command).
  #For RegExp's, command trigger is actually the match object.
  matchCommands = []
  #We have to do three different kinds of check:
  #  Prefix: commands such as 'Hi fellows!
  #  RegExp:   complex commands that need their prefix parsed as a re.  Note, the handler assumes these are
  #            pre-compiled.
  #  Word: command such as say Hi fellows!

  #This will find exact matches.  Usually the user is going to be typing the exact command, and we want that to take
  #precedence.
  #First, prefixes.
  matchCommands.extend([
                      (command, message[:len(command[1])], message[len(command[1]):]) for command in commands if
                      ((command[3] == COMMANDMODE_PREFIX) and
                      (FixCase(message, command[4]).startswith(FixCase(command[1], command[4]))) and
                      (HasPermissions(avatar, command[6])))
                      ])
  #Next, regexp's.  These are slow, but there shouldn't be many of them, so we can just do a for loop.
  reCommands = [command for command in commands if
               ((command[3] == COMMANDMODE_REGEXP) and
               (HasPermissions(avatar, command[6])))]
  for reCommand in reCommands:
    matchObj = reCommand[1].match(message)
    if not(matchObj is None):
      matchCommands.append((reCommand, matchObj, message[matchObj.end():]))
  #Last, normal commands.
  messageSplit = message.split(" ", 1)
  commandTrig = messageSplit[0]
  #If there's no space, add some empty arguments.
  if len(messageSplit) == 1:
    commandArgs = ""
  else:
    commandArgs = messageSplit[1]
  matchCommands.extend([
                      (command, commandTrig, commandArgs) for command in commands if
                      ((command[3] == COMMANDMODE_WORD) and
                      (FixCase(commandTrig, command[4]) == FixCase(command[1], command[4])) and
                      (HasPermissions(avatar, command[6])))
                      ])
  if len(matchCommands) > 0:
    matchCommands.sort()
    matchCommands.reverse()
    if HandleCommands(avatar, matchCommands):
      return None
  #If we're at this point, it means we didn't get an exact match.
  #We can only do inexact matches with Word type commands.
  matchCommands = [(command, commandTrig, commandArgs) for command in commands if
                  ((command[3] == COMMANDMODE_WORD) and
                  (FixCase(command[1], command[4]).startswith(FixCase(commandTrig, command[4]))) and
                  (HasPermissions(avatar, command[6])))
                  ]
  if len(matchCommands) > 0:
    matchCommands.sort()
    matchCommands.reverse()
    if HandleCommands(avatar, matchCommands):
      return None
  #If we're at this point, it means no command handled things.
  #Send(avatar, "I don't understand what you typed.\r\n")

#Attempts to parse the commands.
def HandleCommands(avatar, commandTuples):
  for commandTuple in commandTuples:
    parseResult = MatchCommandText(avatar, commandTuple[2], commandTuple[0][2])
    if parseResult[0]:
      execResult = commandTuple[0][5](avatar, parseResult[1])
      if execResult:
        return True
  return False

#Recursively attempts to match command text.
def MatchCommandText(avatar, userText, commandText):
  #All parse structures are encapsulated in two % signs.
  #Two % signs back to back escapes a single % sign.
  #Parse structures can except arguments in <> brackets; these arguments are passed as a string to the
  #parse function.
  #For example, the matchText string "%bunnynumber%:die %deathmethod<horrible>%" would probably match:
  #  1:die cancer
  #  3:die ebola
  #  14:die pokemon
  #Depending on how the %bunnynumber% and %deathmethod% parse structures handle things.
  MODE_ANCHOR, MODE_PARSE = range(2)
  curMode = MODE_ANCHOR
  matchDataList = []
  #First, we split everything along % lines.
  commandSplit = commandText.split("%")
  #Now, we alternate using anchors and parse structures over the commandSplit.
  for subText in commandSplit:
    if curMode is MODE_ANCHOR:
      if userText.startswith(subText):
        userText = userText[len(subText):]
        curMode = MODE_PARSE
        continue
      else:
        #Couldn't find the anchor.
        return (False, None)
    else:
      #First, check and see if this is an escaped percentage sign.
      if subText == "":
        #Since it is, we parse it as an anchor.
        if userText.startswith("%"):
          userText = userText[1:]
          continue
      #It's not, so lets check the parse table for it.
      #We have to separate out an arguments string(if any).
      parseSplit = subText.split("<", 1)
      parseName = parseSplit[0]
      if len(parseSplit) > 1:
         #Remove ending >.  If its not there, this'll behave screwy, so double check your argument thingies.
         argString = parseSplit[1][:-1]
      else:
        argString = ""
      #Find if the parse function is defined.
      if not(parseTypes.has_key(parseName)):
        return (False, None)
      #Now, LETS PARSE!
      (success, userText, matchData) = parseTypes[parseName](avatar, userText, argString)
      if not(success):
        return (False, None)
      matchDataList.append(matchData)
      curMode = MODE_ANCHOR
  return (True, matchDataList)

def GenerateCommands(avatar):
  commands = []
  #Eventually, check node and objects for commands as well.
  for commandList in avatar.commandLists:
    commands.extend(commandList)
  return commands