text: // Copyright(c) 2019 - 2020, #Momo
  // All rights reserved.
  // 
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions are met :
  // 
  // 1. Redistributions of source code must retain the above copyright notice, this
  // list of conditions and the following disclaimer.
  // 
  // 2. Redistributions in binary form must reproduce the above copyright notice,
  // this list of conditions and the following disclaimer in the documentation
  // and /or other materials provided with the distribution.
  // 
  // 3. Neither the name of the copyright holder nor the names of its
  // contributors may be used to endorse or promote products derived from
  // this software without specific prior written permission.
  // 
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  // DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  // DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  // OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
  #pragma once
  
  #include "Utilities/Time/Time.h"
  #include "Core/Events/EventBase.h"
  #include "Core/Rendering/RenderAdaptor.h"
  #include "Core/MxObject/MxObject.h"
  #include "Utilities/FileSystem/File.h"
  #include "Core/Config/Config.h"
  #include "Utilities/Profiler/Profiler.h"
  #include "Platform/Window/Window.h"
  
  GENERATE_METHOD_CHECK(OnUpdate, OnUpdate(float()));
  
  namespace MxEngine
  {
      class RuntimeEditor;
  
      class Application
      {
          struct ModuleManager
          {
              ModuleManager(Application* app);
              ~ModuleManager();
          } manager;
  
          using UpdateCallbackList = MxVector<void(*)(TimeStep)>;
          using CollisionList = MxVector<std::pair<MxObject::Handle, MxObject::Handle>>;
          using CollisionSwapPair = std::pair<CollisionList, CollisionList>;
      private:
          static inline Application* Current = nullptr;
          UniqueRef<Window> window;
          RenderAdaptor renderAdaptor;
          EventDispatcherImpl<EventBase>* dispatcher;
          RuntimeEditor* editor;
          UpdateCallbackList updateCallbacks;
          CollisionSwapPair collisions;
          Config config;
          TimeStep timeDelta = 0.0f;
          size_t counterFPS = 0;
          TimeStep totalElapsedTime = 0.0f;
          bool shouldClose = false;
          bool isRunning = false;
  
          void InitializeConfig(Config& config);
          void InitializeRuntime(RuntimeEditor& editor);
          void InitializeRenderAdaptor(RenderAdaptor& adaptor);
          void DestroyRenderAdaptor(RenderAdaptor& adaptor);
          void InitializeShaderDebug();
          void UpdateTimeDelta(TimeStep& lastFrameEnd, TimeStep& lastSecondEnd, size_t& framesPerSecond);
          void DrawObjects();
          void UpdateComponents();
          void InvokeUpdate();
          void InvokePhysics();
          void InvokeCreate();
          void CreateContext();
          bool VerifyApplicationState();
      protected:
  
          Application();
  
          virtual void OnCreate();
          virtual void OnUpdate();
          virtual void OnRender();
          virtual void OnDestroy();
      public:
          bool IsPaused = false;
          float TimeScale = 1.0f;
  
          template<typename T>
          void RegisterComponentUpdate();
  
          void ToggleRuntimeEditor(bool isVisible);
          void ToggleWindowUpdates(bool isPolled);
          void CloseOnKeyPress(KeyCode key);
  
          void AddCollisionEntry(const MxObject::Handle& object1, const MxObject::Handle& object2);
          EventDispatcherImpl<EventBase>& GetEventDispatcher();
          RenderAdaptor& GetRenderAdaptor();
          RuntimeEditor& GetRuntimeEditor();
          Config& GetConfig();
          Window& GetWindow();
          TimeStep GetTimeDelta() const;
          TimeStep GetUnscaledTimeDelta() const;
          TimeStep GetTotalElapsedTime() const;
          void SetTotalElapsedTime(TimeStep time);
          size_t GetCurrentFPS() const;
          void Run();
          bool IsRunning() const;
          void CloseApplication();
          virtual ~Application();
  
          static void Init();
          static Application* GetImpl();
          static void Clone(Application* application);
      };
      
      template<typename T>
      inline void Application::RegisterComponentUpdate()
      {
          if constexpr (has_method_OnUpdate<T>::value)
          {
              this->updateCallbacks.push_back([](TimeStep dt)
              {
                  MAKE_SCOPE_PROFILER(typeid(T).name());
                  auto view = ComponentFactory::GetView<T>();
                  for (auto& component : view)
                  {
                      component.OnUpdate(dt);
                  }
              });
          }
      }
  
      #if defined(MXENGINE_PROJECT_SOURCE_DIRECTORY) && defined(MXENGINE_PROJECT_BINARY_DIRECTORY)
      inline void LaunchFromSourceDirectory()
      {
          FilePath currentWorkingDirectory = std::filesystem::current_path();
          FilePath binaryDirectory = MXENGINE_PROJECT_BINARY_DIRECTORY;
          if (currentWorkingDirectory == binaryDirectory)
          {
              std::filesystem::current_path(MXENGINE_PROJECT_SOURCE_DIRECTORY);
          }
      }
      #endif
  }