text: import time
  import threading
  import inspect
  from beautifultable import BeautifulTable
  
  from logger import Logger
  from config import Config
  from messenger import Messenger
  from utils.misc import hms
  from version import __version__
  
  
  class GameStats:
      def __init__(self):
          self._config = Config()
          self._messenger = Messenger()
          self._start_time = time.time()
          self._timer = None
          self._timepaused = None
          self._paused = False
          self._game_counter = 0
          self._chicken_counter = 0
          self._death_counter = 0
          self._merc_death_counter = 0
          self._runs_failed = 0
          self._failed_game_time = 0
          self._location = None
          self._location_stats = {}
          self._stats_filename = f'stats_{time.strftime("%Y%m%d_%H%M%S")}.log'
          self._game_counter_breaktime = 0
          self._kill_count_pindle = 0
          self._kill_count_eld = 0
          self._kill_count_shenk = 0
          self._kill_count_triv = 0
          self._kill_count_nihlatak = 0
          self._kill_count_arc = 0;
          self._route_config = self._config.routes
          
      def update_location(self, loc: str):
          if self._location != loc:
              self._location = str(loc)
              self.populate_location_stat()
              
      def get_location_msg(self):
          if self._location is not None:
              return f" at {self._location}"
          else: 
              return ""
  
      def _send_message_thread(self, msg: str):
          if self._config.general["custom_message_hook"]:
              send_message_thread = threading.Thread(
                  target=self._messenger.send,
                  kwargs={"msg": msg}
              )
              send_message_thread.daemon = True
              send_message_thread.start()
  
      def populate_location_stat(self):
          if self._location not in self._location_stats:
              self._location_stats[self._location] = { "items": [], "deaths": 0, "chickens": 0, "merc_deaths": 0, "failed_runs": 0 }
  
      def log_item_keep(self, item_name: str, item_name_kor: str, send_message: bool):
          filtered_items = ["_potion", "misc_gold"]
          if self._location is not None and not any(substring in item_name for substring in filtered_items):
              self._location_stats[self._location]["items"].append(item_name)
  
          if send_message:
              msg = f"{self._config.general['name']}: Found {item_name_kor}{self.get_location_msg()}"
              self._send_message_thread(msg)
  
      def log_death(self):
          self._death_counter += 1
          if self._location is not None:
              self._location_stats[self._location]["deaths"] += 1
          msg = f"{self._config.general['name']}: You have died{self.get_location_msg()}"
          self._send_message_thread(msg)
  
      def log_chicken(self):
          self._chicken_counter += 1
          if self._location is not None:
              self._location_stats[self._location]["chickens"] += 1
          msg = f"{self._config.general['name']}: You have chickened{self.get_location_msg()}"
          self._send_message_thread(msg)
  
      def log_merc_death(self):
          self._merc_death_counter += 1
          if self._location is not None:
              self._location_stats[self._location]["merc_deaths"] += 1
  
      def log_start_game(self):
          if self._game_counter > 0:
              self._save_stats_to_file()
              if self._config.general["discord_status_count"] and self._game_counter % self._config.general["discord_status_count"] == 0:
                  # every discord_status_count game send a message update about current status
                  self._send_status_update()
          self._game_counter += 1
          self._game_counter_breaktime += 1
          self._timer = time.time()
          Logger.info(f"Starting game #{self._game_counter}")
  
      def log_end_game(self, failed: bool = False):
          elapsed_time = 0
          if self._timer is not None:
              elapsed_time = time.time() - self._timer
          self._timer = None
          if failed:
              self._runs_failed += 1
              if self._location is not None:
                  self._location_stats[self._location]["failed_runs"] += 1
              self._failed_game_time += elapsed_time
              Logger.warning(f"End failed game: Elpased time: {elapsed_time:.2f}s")
          else:
              Logger.info(f"End game. Elapsed time: {elapsed_time:.2f}s")
              
          if self._config.general["discord_run_count"]:
              data = f"{self._config.general['name']}: runs={self._game_counter} [ "
              dataArray = [];
              if self._route_config["run_pindle"]:
                  dataArray.append( f"Pin={self._kill_count_pindle}" )
              if self._route_config["run_eldritch"]:
                  dataArray.append( f"Eld={self._kill_count_eld}" )
              if self._route_config["run_shenk"]:
                  dataArray.append( f"Shk={self._kill_count_shenk}" )
              if self._route_config["run_trav"]:
                  dataArray.append( f"Trav={self._kill_count_triv}" )
              if self._route_config["run_nihlatak"]:
                  dataArray.append( f"Nihl={self._kill_count_nihlatak}" )
              if self._route_config["run_arcane"]:
                  dataArray.append( f"Arc={self._kill_count_arc}" )
              data += ', '.join( dataArray )
              data += " ]"
              self._send_message_thread( data );
  
      def pause_timer(self):
          if self._timer is None or self._paused:
              return
          self._timepaused = time.time()
          self._paused = True
  
      def resume_timer(self):
          if self._timer is None or not self._paused:
              return
          pausetime = time.time() - self._timepaused
          self._timer = self._timer + pausetime
          self._paused = False
  
      def get_current_game_length(self):
          if self._timer is None:
              return 0
          if self._paused:
              return self._timepaused - self._timer
          else:
              return time.time() - self._timer
  
      def _create_msg(self):
          elapsed_time = time.time() - self._start_time
          elapsed_time_str = hms(elapsed_time)
          avg_length_str = "n/a"
          good_games_count = self._game_counter - self._runs_failed
          if good_games_count > 0:
              good_games_time = elapsed_time - self._failed_game_time
              avg_length = good_games_time / float(good_games_count)
              avg_length_str = hms(avg_length)
          msg = inspect.cleandoc(f'''
              Session length: {elapsed_time_str}
              Games: {self._game_counter}
              Avg Game Length: {avg_length_str}
          ''')
          totals = { "items": 0, "chickens": 0, "deaths": 0, "merc_deaths": 0, "failed_runs": 0 }
          table = BeautifulTable()
          table.set_style(BeautifulTable.STYLE_BOX_ROUNDED)
          for location in self._location_stats:
              stats = self._location_stats[location]
              totals["items"] += len(stats["items"])
              totals["chickens"] += stats["chickens"]
              totals["deaths"] += stats["deaths"]
              totals["merc_deaths"] += stats["merc_deaths"]
              totals["failed_runs"] += stats["failed_runs"]
              table.rows.append([location, len(stats["items"]), stats["chickens"], stats["deaths"], stats["merc_deaths"], stats["failed_runs"]])
          table.rows.append([
              "T" if self._config.general['discord_status_condensed'] else "Total",
              totals["items"],
              totals["chickens"],
              totals["deaths"],
              totals["merc_deaths"],
              totals["failed_runs"]
          ])
          if self._config.general['discord_status_condensed']:
              table.columns.header = ["Run", "I", "C", "D", "MD", "F"]
          else:
              table.columns.header = ["Run", "Items", "Chicken", "Death", "Merc Death", "Failed Runs"]
  
          msg += f"\n{str(table)}\n"
          return msg
  
      def _send_status_update(self):
          msg = f"{self._config.general['name']}: Status Report\\n{self._create_msg()}\\nVersion: {__version__}"
          self._send_message_thread(msg)
  
      def _save_stats_to_file(self):
          msg = self._create_msg()
          msg += "\nItems:"
          for location in self._location_stats:
              stats = self._location_stats[location]
              msg += f"\n  {location}:"
              for item_name in stats["items"]:
                  msg += f"\n    {item_name}"
  
          with open(file=f"stats/{self._stats_filename}", mode="w+", encoding="utf-8") as f:
              f.write(msg)
              
      def log_kill_pindle(self):
          self._kill_count_pindle += 1
      
      def log_kill_eld(self):
          self._kill_count_eld += 1
          
      def log_kill_shenk(self):
          self._kill_count_shenk += 1
          
      def log_kill_triv(self):
          self._kill_count_triv += 1
          
      def log_kill_nihlatak(self):
          self._kill_count_nihlatak += 1
          
      def log_kill_arc(self):
          self._kill_count_arc += 1
          
      def reset_game(self):
          self._game_counter_breaktime = 0
  
  
  if __name__ == "__main__":
      game_stats = GameStats()
      game_stats.log_item_keep("rune_12", True)
      game_stats._save_stats_to_file()
  