import zlib

_imports = ["lib_markup"]
_parents = []
_version = 1

#The avatar object handles negotiation of mud client protocols and formatting of the the text between server and
#client to fit those protocols.  Once that is done, it's derived classes handle input and output between the client.

ANSIColorMapping = {"-c":"\x1B[0m", "b+":"\x1B[1m", "b-":"\x1B[22m", "i+":"\x1B[3m", "i-":"\x1B[23m", "u+":"\x1B[4m",
                    "u-":"\x1B[24m", "n+":"\x1B[7m", "n-":"\x1B[27m", "s+":"\x1B[9m", "s-":"\x1B[29m", "fz":"\x1B[30m",
                    "fr":"\x1B[31m", "fg":"\x1B[32m", "fy":"\x1B[33m", "fb":"\x1B[34m", "fp":"\x1B[35m",
                    "fc":"\x1B[36m", "fw":"\x1B[37m", "fd":"\x1B[39m", "fZ":"\x1B[1m\x1B[30m", "fR":"\x1B[1m\x1B[31m",
                    "fG":"\x1B[1m\x1B[32m", "fY":"\x1B[1m\x1B[33m", "fB":"\x1B[1m\x1B[34m", "fP":"\x1B[1m\x1B[35m",
                    "fC":"\x1B[1m\x1B[36m", "fW":"\x1B[1m\x1B[37m", "b0":"\x1B[40m", "br":"\x1B[41m", "bg":"\x1B[42m",
                    "by":"\x1B[43m", "bb":"\x1B[44m", "bp":"\x1B[45m", "bc":"\x1B[46m", "bw":"\x1B[47m", "bd":"\x1B[49m"}

MXPMapping = {"xo":"\x1B[0z","xs":"\x1B[1z","xl":"\x1B[2z", "xr":"\x1B[3z", "xn":"\x1B[4z", "lo":"\x1B[5z", "ls":"\x1B[6z",
              "ll":"\x1B[7z"}
              
ansiTypes = ["zmud", "vt100", "ansi"]

def _Sys_Create(self, client):
  self._persist = True
  self.client = client
  self.useMXP = False
  self.useMXPSound = False
  self.useMSP = False
  self.useMCCP = False
  self.useMCCP2 = False
  self.useANSI = False
  self.sendBuffer = ""
  self.MXPOnly = False
  self.notMXPOnly = False
  self.terminalType = ""
  self.permissions = {}

def Avatar_Connect(self):
  #IAC WILL TERMINALTYPE, IAC WILL MXP, IAC WILL MSP, IAC WILL COMPRESS2, IAC WILL COMPRESS
  sendString = "\xFF\xFB\x18\xFF\xFB\x5B\xFF\xFB\x5A\xFF\xFB\x56\xFF\xFB\x55"
  self.Avatar_RawSend(sendString)

#Abstract: called upon client going linkdead.
def Avatar_Linkdeath(self):
  pass

#Abstract: called upon client being disconnected by the game, IE by a quit command.
def Avatar_Disconnect(self):
  pass

def _Sys_TelnetDO(self, doType):
  #DO MXP
  if doType == "\x5B":
    self.useMXP = True
    self.useMXPSound = True
  #DO MSP
  elif doType == "\x5A":
    self.useMSP = True
  #DO COMPRESS2
  elif doType == "\x56":
    if self.useMCCP == True:
      self.useMCCP = False
    #Send the client our compression stream notice.
    #IAC SB COMPRESS2 IAC SE
    self.Avatar_RawSend("\xFF\xFA\x56\xFF\xF0")
    self.useMCCP2 = True
    self.compressObj = zlib.compressobj()
  #DO COMPRESS
  elif doType == "\x55":
    #If we're already using MCCP2, no need to use MCCP.
    if self.useMCCP2 == False:
      #Same as above.
      #IAC SB COMPRESS WILL SE - Note that the SE is not prefixed by WILL, not IAC
      self.Avatar_RawSend("\xFF\xFA\x56\xFB\xF0")
      self.useMCCP = True
      self.compressObj = zlib.compressobj()
  #DO TERMINALTYPE
  elif doType == "\x18":
    #IAC SB TERMINALTYPE SEND IAC SE
    self.Avatar_RawSend("\xFF\xFA\x18\x01\xFF\xF0")

def _Sys_TelnetDONT(self, dontType):
  #DONT MXP
  if dontType == "\x5B":
    self.useMXP = False
    self.useMXPSound = False
  #DONT MSP
  elif dontType == "\x5A":
    self.useMSP = False
  #DONT COMPRESS2
  elif dontType == "\x56":
    if self.useMCCP2:
      self.client.Send(self.compressObj.flush(zlib.Z_FINISH))
      self.useMCCP2 = False
      self.compressObj = None
  #DONT COMPRESS
  elif dontType == "\x55":
    if self.useMCCP:
      self.client.Send(self.compressObj.flush(zlib.Z_FINISH))
      self.useMCCP = False
      self.compressObj = None

def _Sys_ReceiveCommand(self, command):
  pass

def _Sys_TelnetSB(self, subCommand):
  #TERMINALTYPE IS
  if subCommand.startswith("\x18\x00"):
    terminalType = subCommand[2:]
    self.terminalType = terminalType
    if terminalType in ansiTypes:
      self.useANSI = True

def Avatar_Send(self, message):
  self.sendBuffer = ""
  #Deal with escaped characters.

  escIndex = message.find(lib_markup.cEsc)
  newMessage = ""
  while escIndex != -1:
    escape = message[escIndex+1:escIndex+3]
    newMessage = "".join((newMessage, self.Parse(message[:escIndex])))
    message = message[(escIndex+3):]
    if escape in ANSIColorMapping:
      if self.useANSI:
        newMessage = "".join((newMessage, ANSIColorMapping[escape]))
    elif escape in MXPMapping:
      if self.useMXP:
        newMessage = "".join((newMessage, MXPMapping[escape]))
    elif escape == "--":
      self.MXPOnly = False
      self.notMXPOnly = False
      newMessage = "".join((newMessage, ANSIColorMapping["-c"]))
    elif escape == "-x":
      self.MXPOnly = False
      self.notMXPOnly = False
    elif escape == "x+":
      self.MXPOnly = True
      self.notMXPOnly = False
    elif escape == "x-":
      self.MXPOnly = False
    elif escape == "o+":
      self.notMXPOnly = True
      self.MXPOnly = False
    elif escape == "o-":
      self.notMXPOnly = False
    elif escape == "t+":
      if self.useMXP:
        newMessage = "".join((newMessage, "<"))
      searchIndex = message.find("".join((lib_markup.cEsc, "t-")))
      if searchIndex != -1:
        if self.useMXP:
          newMessage = "".join((newMessage, "".join((message[:searchIndex], ">"))))
        message = message[(searchIndex + 3):]
      else:
        mudWorld.logs["mud"].error("Unclosed MXP tag.")
    elif escape == "sd":
      searchIndex = message.find(">")
      if searchIndex != -1:
        #MXP takes precedence.
        if self.useMXPSound:
          newMessage = "".join((newMessage, MXP("SOUND " + message[1:searchIndex])))
          message = message[(searchIndex + 1):]
        elif self.useMSP:
          newMessage = "".join(("!!SOUND(", message[1:searchIndex], ")"))
          message = message[(searchIndex + 1):]
      else:
        mudWorld.logs["mud"].error("Sound string improperly ended, can not parse.")
    elif escape == "ms":
      searchIndex = message.find(">")
      if searchIndex != -1:
        #MXP takes precedence.
        if obj["useMXPSound"]:
          newMessage = "".join((newMessage, MXP("MUSIC ", message[1:searchIndex])))
          message = message[(searchIndex + 1):]
        elif obj["useMSP"]:
          newMessage = "".join(("!!MUSIC(", message[1:searchIndex], ")"))
          message = message[(searchIndex + 1):]
      else:
        mudWorld.logs["mud"].error("Music string improperly ended, can not parse.")
    else:
      mudWorld.logs["mud"].error("Unrecognized escape %s" % escape)
    escIndex = message.find(lib_markup.cEsc)
  newMessage = "".join((newMessage, self.Parse(message)))
  if self.useMCCP or self.useMCCP2:
    newMessage = self.compressObj.compress(newMessage)
    newMessage = "".join((newMessage, self.compressObj.flush(zlib.Z_SYNC_FLUSH)))
  self.client.Send(newMessage)

def Avatar_RawSend(self, sendString):
  if self.useMCCP or self.useMCCP2:
    sendString = self.compressObj.compress(sendString)
    sendString = "".join((sendString, self.compressObj.flush(zlib.Z_SYNC_FLUSH)))
  self.client.Send(sendString)

#Private function section.

def Parse(self, message):
  if self.MXPOnly:
    if self.useMXP:
      message = self.ParseMXP(message)
    else:
      message = ""
  elif self.notMXPOnly:
    if self.useMXP:
      message = ""
  elif self.useMXP:
    message = self.ParseMXP(message)
  return message

def ParseMXP(self, message):
  message.replace("<", "&lt;")
  message.replace(">", "&gt;")
  message.replace("&", "&amp;")
  message.replace("\"", "&quot")
  return message