text: # -*- coding: utf-8 -*-
  import codecs
  import re
  import struct
  from urllib.parse import unquote
  
  import requests
  
  
  class TenhouParser(object):
      def get_player_names(self, log_id):
          log_id = self._get_log_name_for_download(log_id)
          log_content = self._download_log(log_id)
          rounds = self._parse_rounds(log_content)
  
          players = []
          first_round = rounds[0]
          for tag in first_round:
              if tag.startswith("<UN"):
                  players = [
                      unquote(self._get_attribute_content(tag, "n0")),
                      unquote(self._get_attribute_content(tag, "n1")),
                      unquote(self._get_attribute_content(tag, "n2")),
                      unquote(self._get_attribute_content(tag, "n3")),
                  ]
                  break
  
          return players
  
      def _download_log(self, log_id):
          headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"}
          response = requests.get("http://e.mjv.jp/0/log/?{}".format(log_id), headers=headers)
          data = response.text
          return data
  
      def _get_log_name_for_download(self, log_id):
          table = [
              22136,
              52719,
              55146,
              42104,
              59591,
              46934,
              9248,
              28891,
              49597,
              52974,
              62844,
              4015,
              18311,
              50730,
              43056,
              17939,
              64838,
              38145,
              27008,
              39128,
              35652,
              63407,
              65535,
              23473,
              35164,
              55230,
              27536,
              4386,
              64920,
              29075,
              42617,
              17294,
              18868,
              2081,
          ]
  
          code_pos = log_id.rindex("-") + 1
          code = log_id[code_pos:]
          if code[0] == "x":
              a, b, c = struct.unpack(">HHH", bytes.fromhex(code[1:]))
              index = 0
              if log_id[:12] > "2010041111gm":
                  x = int("3" + log_id[4:10])
                  y = int(log_id[9])
                  index = x % (33 - y)
              first = (a ^ b ^ table[index]) & 0xFFFF
              second = (b ^ c ^ table[index] ^ table[index + 1]) & 0xFFFF
              result = codecs.getencoder("hex_codec")(struct.pack(">HH", first, second))[0].decode("ASCII")
              return log_id[:code_pos] + result
          else:
              return log_id
  
      def _parse_rounds(self, log_content):
          tag_start = 0
          rounds = []
          tag = None
          game_round = []
          for x in range(0, len(log_content)):
              if log_content[x] == ">":
                  tag = log_content[tag_start : x + 1]
                  tag_start = x + 1
  
              # not useful tags
              if tag and ("mjloggm" in tag or "TAIKYOKU" in tag or "SHUFFLE seed=" in tag):
                  tag = None
  
              # new round was started
              if tag and "INIT" in tag:
                  rounds.append(game_round)
                  game_round = []
  
              # the end of the game
              if tag and "owari" in tag:
                  rounds.append(game_round)
  
              if tag:
                  # to save some memory we can remove not needed information from logs
                  if "INIT" in tag:
                      # we dont need seed information
                      find = re.compile(r'shuffle="[^"]*"')
                      tag = find.sub("", tag)
  
                  # add processed tag to the round
                  game_round.append(tag)
                  tag = None
  
          return rounds
  
      def _get_attribute_content(self, message, attribute_name):
          result = re.findall(r'{}="([^"]*)"'.format(attribute_name), message)
          return result and result[0] or None
  