import re

from const_commands import *

_imports = ["commandlib_user", "commandlib_admin", "lib_commandparser"]
_parents = []
_version = 1

lastError = None

#A note on command prefixes:
#  No prefix - Used for IC commands.
#  @command - Used for OOC commands.
#  $command - Used for building commands.
#  *command - Used for GM commands.
#  %command - Used for administrative commands.

#Parse type definitions.

#Grabs all text following a command.
def Parse_rawtext(avatar, userText, args):
  return (True, "", userText)

#Grabs all text following a command, as long as its length is greater than 0.
def Parse_sometext(avatar, userText, args):
  if len(userText) > 0:
    return (True, "", userText)
  else:
    return (False, "Must include text after command.", None)

numRe = re.compile("^[0-9]+")
#Grabs a single base10 integer.  Returns in integer form.
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, "\"%s\" is not a number." % userText, None)

#Grabs a valid object type.
#Returns the objtype string.
def Parse_objtype(avatar, userText, args):
  splitList = userText.split(" ", 1)
  if len(splitList) > 1:
    remainder = userText[len(splitList[0]):]
  else:
    remainder = ""
  objType = splitList[0]
  if mudWorld.objIndex.has_key(objType):
    return (True, remainder, objType)
  else:
    return (False, "\"%s\" is not a valid object type." % objType, None)

parseTypes = {
"raw_text":Parse_rawtext, "some_text":Parse_sometext, "num":Parse_num, "object_type":Parse_objtype
}

welcome = "\r\n\r\nWelcome!  Type @commands for a list of commands.\r\n"
#[priority, word or prefix or regexp, textMatch, commandMode, caseSensitive, commandFunc, permissions]


def EmptyParser(avatar, message):
  pass

def TakeControl(avatar):
  avatar.usePrompt = True
  avatar.commandModules = [commandlib_user, commandlib_admin]
  avatar.Send(welcome)
  avatar.handler = (lib_commandparser, "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.
  if lastError is None:
    avatar.Send("I don't understand what you typed.\r\n")
  else:
    avatar.Send("Parse error: %s\r\n" % lastError)
    globals()["lastError"] = None

#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, commandTuple[1], 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)):
        mudWorld.loggers["mud"].error("Invalid parse type [%s]." % parseName)
        return (False, None)
      #Now, LETS PARSE!
      (success, userText, matchData) = parseTypes[parseName](avatar, userText, argString)
      if not(success):
        if globals()["lastError"] == None:
          globals()["lastError"] = userText
        return (False, None)
      matchDataList.append(matchData)
      curMode = MODE_ANCHOR
  #Return False if all the data hasn't been usen.
  if len(userText.strip()):
    if globals()["lastError"] == None:
      globals()["lastError"] = "Invalid arguments following command."
    return (False, None)
  return (True, matchDataList)

def GenerateCommands(avatar):
  commands = []
  commands.extend(avatar.ExportCommands())
  return commands