text: ## ========================================================================= ##
  ## Copyright (c) 2019 Agustin Durand Diaz.                                   ##
  ## This code is licensed under the MIT license.                              ##
  ## application.py                                                            ##
  ## ========================================================================= ##
  
  import pygame
  import settings
  from gui.screen_manager import ScreenManager
  from enums import ScreenType, SimulationType
  
  class Application(object):
      def __init__(self):
          pygame.init() #pygame.font.init() is called here
  
          icon = pygame.image.load('assets/brain.png')
          pygame.display.set_icon(icon)
          pygame.display.set_caption('NEUROEVOLVER')
  
          self.screen = pygame.display.set_mode((settings.APP_WIDTH, settings.APP_HEIGHT), pygame.DOUBLEBUF)
          settings.OBJ_SURFACE = self.screen
  
          self.clock = pygame.time.Clock()
          self.last_tick = pygame.time.get_ticks()
  
          self.m_screenManager = ScreenManager(settings.APP_WIDTH, settings.APP_HEIGHT)
  
          loadAssets()
  
      def play(self):
          self.m_screenManager.gotoScreen(ScreenType.MAIN_MENU)
          #self.m_screenManager.gotoScreen(ScreenType.SIMULATION, {'simulationType': SimulationType.NEAT_B2D_WALKER})
          self.onLoop()
  
      def onLoop(self):
  
          forceToRepaintAllScreen = False
          is_running = True
  
          while is_running:
              # limits updates to settings.APP_FPS(=60) frames per second (FPS)
              self.clock.tick(settings.APP_FPS)
              for event in pygame.event.get():
                  if event.type == pygame.QUIT \
                      or event.type == settings.NEAT_IP_EVENT_END_EVOLVING \
                      or event.type == settings.NEAT_DYCICLE_EVENT_END_EVOLVING \
                      or event.type == settings.NEAT_DIP_EVENT_END_EVOLVING \
                      or event.type == settings.NEAT_WALKER_EVENT_END_EVOLVING \
                      or event.type == settings.NEAT_TIP_EVENT_END_EVOLVING:
                      is_running = False
                  if event.type == pygame.KEYDOWN:
                      self.m_screenManager.onKeyPress(event.key)
                      if event.key == pygame.K_1:
                          settings.SHOW_DEBUG_SHAPES = not settings.SHOW_DEBUG_SHAPES
                          forceToRepaintAllScreen = True
                  if event.type == pygame.KEYUP:
                      self.m_screenManager.onKeyRelease(event.key)
                  if event.type == pygame.MOUSEMOTION:
                      self.m_screenManager.onMouseMove(event)
                  if event.type == pygame.MOUSEBUTTONDOWN:
                      self.m_screenManager.onMouseDown(event)
                  if event.type == pygame.MOUSEBUTTONUP:
                      self.m_screenManager.onMouseRelease(event)
                  if event.type == pygame.VIDEOEXPOSE:
                      self.m_screenManager.forceRedraw()
  
              actual_tick = pygame.time.get_ticks()
              self.m_screenManager.updateTime(actual_tick - self.last_tick)
              self.last_tick = actual_tick
              dirtyRects = self.m_screenManager.draw(self.screen)
  
              if forceToRepaintAllScreen:
                  forceToRepaintAllScreen = False
                  pygame.display.flip()
                  continue
  
              if settings.SHOW_DEBUG_SHAPES:
                  pygame.display.flip()
              else:
                  pygame.display.update(dirtyRects)
  
          #pygame.quit()
          #sys.exit()
  
          if len(settings.TRAIN_MODE) != 0:
              tm = settings.TRAIN_MODE
              settings.TRAIN_MODE = ''
              settings.TRAIN_CALLBACK(tm)
  
      def trainNeatIP(self, genomes, config, currentStep):
          params = {}
          params['isTraining'] = True
          params['currentStep'] = currentStep
          params['genomes'] = genomes
          params['config'] = config
          params['simulationType'] = SimulationType.NEAT_INVERTED_PENDULUM
          self.m_screenManager.gotoScreen(ScreenType.SIMULATION, params)
          self.onLoop()
  
      def trainNeatDIP(self, genomes, config, currentStep):
          params = {}
          params['isTraining'] = True
          params['currentStep'] = currentStep
          params['genomes'] = genomes
          params['config'] = config
          params['simulationType'] = SimulationType.NEAT_B2D_DIP
          self.m_screenManager.gotoScreen(ScreenType.SIMULATION, params)
          self.onLoop()
  
      def trainNeatTIP(self, genomes, config, currentStep):
          params = {}
          params['isTraining'] = True
          params['currentStep'] = currentStep
          params['genomes'] = genomes
          params['config'] = config
          params['simulationType'] = SimulationType.NEAT_B2D_TIP
          self.m_screenManager.gotoScreen(ScreenType.SIMULATION, params)
          self.onLoop()
  
      def trainNeatDycicle(self, genomes, config, currentStep):
          params = {}
          params['isTraining'] = True
          params['currentStep'] = currentStep
          params['genomes'] = genomes
          params['config'] = config
          params['simulationType'] = SimulationType.NEAT_DYCICLE
          self.m_screenManager.gotoScreen(ScreenType.SIMULATION, params)
          self.onLoop()
  
      def trainNeatWalker(self, genomes, config, currentStep):
          params = {}
          params['isTraining'] = True
          params['currentStep'] = currentStep
          params['genomes'] = genomes
          params['config'] = config
          params['simulationType'] = SimulationType.NEAT_B2D_WALKER
          self.m_screenManager.gotoScreen(ScreenType.SIMULATION, params)
          self.onLoop()
  
  def loadAssets():
      if settings.ASSETS_LOADED:
          return
  
      def loadImage(path):
          settings.IMAGES_ALPHA[path] = pygame.image.load(path).convert_alpha()
          settings.IMAGES[path] = pygame.image.load(path).convert()
  
      loadImage('assets/black_btn.png')
  
      settings.ASSETS_LOADED = True