<commit_before>/*
   Filename: EngineUI.cpp
   Purpose:  Draws the EngineUI and console on top of rendering

   Part of Engine2D

   Copyright (C) 2014 Vbitz

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include "EngineUI.hpp"

#include <OpenGL/gl3.h>

#include "FramePerfMonitor.hpp"
#include "Config.hpp"
#include "Profiler.hpp"

#define SEC_TO_NSEC 1000000000

namespace Engine {
    char getSpecialChars(int key) {
        switch (key) {
            case '[':       return '{';
            case ']':       return '}';
            case '-':       return '_';
            case '=':       return '+';
            case '\\':      return '|';
            case '`':       return '~';
            case '1':       return '!';
            case '2':       return '@';
            case '3':       return '#';
            case '4':       return '$';
            case '5':       return '%';
            case '6':       return '^';
            case '7':       return '&';
            case '8':       return '*';
            case '9':       return '(';
            case '0':       return ')';
            case ',':       return '<';
            case '.':       return '>';
            case ';':       return ':';
            case '\'':      return '"';
            case '/':       return '?';
            default:        return key;
        }
    }
    
    double _getHeapUsage() {
        v8::HeapStatistics stats;
        v8::Isolate::GetCurrent()->GetHeapStatistics(&stats);
        return (double) stats.used_heap_size() / (double) stats.total_heap_size();
    }
    
    EngineUI::EngineUI(ApplicationPtr app) : _app(app) {
        this->_draw = new Draw2D(app->GetRender());
        
        for (int i = 0; i < timingResolution; i++) {
            this->_lastDrawTimes[i] = 0.0f;
            this->_lastHeapUsages[i] = 0.0f;
        }
        
        GetEventsSingilton()->GetEvent("onProfileEnd")->AddListener("EngineUI::_profilerHook", EventEmitter::MakeTarget(_profilerHook));
        GetEventsSingilton()->GetEvent("captureLastDrawTimes")->AddListener("EngineUI::_captureLastDrawTimes", EventEmitter::MakeTarget(_captureLastDrawTimes));
        GetEventsSingilton()->GetEvent("logEvent")->AddListener("EngineUI::_createToast", EventEmitter::MakeTarget(_createToast));
    }
    
    void EngineUI::Draw() {
        if (!Config::GetBoolean("core.debug.engineUI")) {
            return;
        }
        
        ENGINE_PROFILER_SCOPE;
        
        RenderDriverPtr renderGL = this->_draw->GetRender();
        WindowPtr window = this->_app->GetWindow();
        glm::vec2 windowSize = window->GetWindowSize();
        
        renderGL->ResetMatrix();
        
        if (!_showConsole) {
            renderGL->SetColor(25 / 255.0f, 25 / 255.0f, 25 / 255.0f);
        } else {
            renderGL->SetColor(40 / 255.0f, 40 / 255.0f, 40 / 255.0f, 0.95f);
        }
        
        this->_draw->Rect(0.0f, 0.0f, windowSize.x, _showConsole ? windowSize.y : 14);
        
        renderGL->SetFont("basic", 10);
        if (!this->_showConsole) {
            renderGL->SetColor(220 / 255.0f, 220 / 255.0f, 220 / 255.0f);
        } else {
            renderGL->SetColor(1.0f, 1.0f, 1.0f);
        }
        
        renderGL->Print(10, 4, "-- Engine2D --");
        
        if (Config::GetBoolean("core.debug.profiler")) {
            double drawTime = FramePerfMonitor::GetDrawTime();
            this->_lastHeapUsages[this->_currentLastDrawTimePos] = _getHeapUsage();
            this->_lastDrawTimes[this->_currentLastDrawTimePos++] = drawTime;
            
            if (this->_currentLastDrawTimePos >= timingResolution) {
                this->_currentLastDrawTimePos = 0;
            }
            
            renderGL->DisableSmooth();
            
            glLineWidth(0.1); // HACK: Until this is exposed by RenderGL
            
            double lineGraphScale = Config::GetFloat("core.debug.engineUI.profilerScale");
            
            this->_draw->LineGraph(windowSize.x - 440, 14, 0.2, lineGraphScale, this->_lastDrawTimes, timingResolution);
            
            renderGL->EnableSmooth();
            
            this->_ss.str("");
            this->_ss << "FPS: " << FramePerfMonitor::GetFPS();
            this->_ss << " | DrawTime (ms): ";
            
            renderGL->Print(windowSize.x - 230, 4, this->_ss.str().c_str());
            
            this->_ss.precision(6);
            this->_ss.str("");
            
            double drawTimeMS = (drawTime * 1000);
            
            this->_ss << (drawTimeMS);
            
            if (drawTimeMS > (1000 / 60)) {
                renderGL->SetColor("red");
            } else {
                renderGL->SetColor("green");
            }
            
            renderGL->Print(windowSize.x - 60, 4, this->_ss.str().c_str());
        }
        
        if (!_showConsole) {
            // Update Toasts
            double dt = FramePerfMonitor::GetFrameTime();
            int y = 30;
            
            for (auto iter = this->_toasts.begin(); iter != this->_toasts.end(); iter++) {
                iter->timeOnScreen += dt;
                if (iter->timeOnScreen < 5.0) {
                    renderGL->SetColor(0.9, 0.9, 0.9, 0.8);
                    this->_draw->Rect(windowSize.x - 260, y, 250, 16);
                    renderGL->SetColor(0.1, 0.1, 0.1, 1.0);
                    this->_draw->Rect(windowSize.x - 260 + 1, y + 1, 14, 14);
                    renderGL->Print(windowSize.x - 260 + 16, y + 2, iter->domain.c_str());
                    renderGL->SetColor(0.3, 0.3, 0.3, 1.0);
                    renderGL->Print(windowSize.x - 260 + 16 + 80, y + 2, iter->info.c_str());
                    y += 18;
                }
            }
            
            return;
        }
        
        if (this->_currentView == CurrentView::Console) {
            std::vector<Logger::LogEvent>* logEvents = Logger::GetEvents();
            
            bool showVerbose = Config::GetBoolean("core.debug.engineUI.showVerboseLog"); // pretty cheap
            
            int i = windowSize.y - 40;
            
            for (auto iterator = logEvents->rbegin(); iterator < logEvents->rend(); iterator++) {
                if (iterator->Hidden) {<commit_msg>Reversed the order of toasts<commit_after>/*
   Filename: EngineUI.cpp
   Purpose:  Draws the EngineUI and console on top of rendering

   Part of Engine2D

   Copyright (C) 2014 Vbitz

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include "EngineUI.hpp"

#include <OpenGL/gl3.h>

#include "FramePerfMonitor.hpp"
#include "Config.hpp"
#include "Profiler.hpp"

#define SEC_TO_NSEC 1000000000

namespace Engine {
    char getSpecialChars(int key) {
        switch (key) {
            case '[':       return '{';
            case ']':       return '}';
            case '-':       return '_';
            case '=':       return '+';
            case '\\':      return '|';
            case '`':       return '~';
            case '1':       return '!';
            case '2':       return '@';
            case '3':       return '#';
            case '4':       return '$';
            case '5':       return '%';
            case '6':       return '^';
            case '7':       return '&';
            case '8':       return '*';
            case '9':       return '(';
            case '0':       return ')';
            case ',':       return '<';
            case '.':       return '>';
            case ';':       return ':';
            case '\'':      return '"';
            case '/':       return '?';
            default:        return key;
        }
    }
    
    double _getHeapUsage() {
        v8::HeapStatistics stats;
        v8::Isolate::GetCurrent()->GetHeapStatistics(&stats);
        return (double) stats.used_heap_size() / (double) stats.total_heap_size();
    }
    
    EngineUI::EngineUI(ApplicationPtr app) : _app(app) {
        this->_draw = new Draw2D(app->GetRender());
        
        for (int i = 0; i < timingResolution; i++) {
            this->_lastDrawTimes[i] = 0.0f;
            this->_lastHeapUsages[i] = 0.0f;
        }
        
        GetEventsSingilton()->GetEvent("onProfileEnd")->AddListener("EngineUI::_profilerHook", EventEmitter::MakeTarget(_profilerHook));
        GetEventsSingilton()->GetEvent("captureLastDrawTimes")->AddListener("EngineUI::_captureLastDrawTimes", EventEmitter::MakeTarget(_captureLastDrawTimes));
        GetEventsSingilton()->GetEvent("logEvent")->AddListener("EngineUI::_createToast", EventEmitter::MakeTarget(_createToast));
    }
    
    void EngineUI::Draw() {
        if (!Config::GetBoolean("core.debug.engineUI")) {
            return;
        }
        
        ENGINE_PROFILER_SCOPE;
        
        RenderDriverPtr renderGL = this->_draw->GetRender();
        WindowPtr window = this->_app->GetWindow();
        glm::vec2 windowSize = window->GetWindowSize();
        
        renderGL->ResetMatrix();
        
        if (!_showConsole) {
            renderGL->SetColor(25 / 255.0f, 25 / 255.0f, 25 / 255.0f);
        } else {
            renderGL->SetColor(40 / 255.0f, 40 / 255.0f, 40 / 255.0f, 0.95f);
        }
        
        this->_draw->Rect(0.0f, 0.0f, windowSize.x, _showConsole ? windowSize.y : 14);
        
        renderGL->SetFont("basic", 10);
        if (!this->_showConsole) {
            renderGL->SetColor(220 / 255.0f, 220 / 255.0f, 220 / 255.0f);
        } else {
            renderGL->SetColor(1.0f, 1.0f, 1.0f);
        }
        
        renderGL->Print(10, 4, "-- Engine2D --");
        
        if (Config::GetBoolean("core.debug.profiler")) {
            double drawTime = FramePerfMonitor::GetDrawTime();
            this->_lastHeapUsages[this->_currentLastDrawTimePos] = _getHeapUsage();
            this->_lastDrawTimes[this->_currentLastDrawTimePos++] = drawTime;
            
            if (this->_currentLastDrawTimePos >= timingResolution) {
                this->_currentLastDrawTimePos = 0;
            }
            
            renderGL->DisableSmooth();
            
            glLineWidth(0.1); // HACK: Until this is exposed by RenderGL
            
            double lineGraphScale = Config::GetFloat("core.debug.engineUI.profilerScale");
            
            this->_draw->LineGraph(windowSize.x - 440, 14, 0.2, lineGraphScale, this->_lastDrawTimes, timingResolution);
            
            renderGL->EnableSmooth();
            
            this->_ss.str("");
            this->_ss << "FPS: " << FramePerfMonitor::GetFPS();
            this->_ss << " | DrawTime (ms): ";
            
            renderGL->Print(windowSize.x - 230, 4, this->_ss.str().c_str());
            
            this->_ss.precision(6);
            this->_ss.str("");
            
            double drawTimeMS = (drawTime * 1000);
            
            this->_ss << (drawTimeMS);
            
            if (drawTimeMS > (1000 / 60)) {
                renderGL->SetColor("red");
            } else {
                renderGL->SetColor("green");
            }
            
            renderGL->Print(windowSize.x - 60, 4, this->_ss.str().c_str());
        }
        
        if (!_showConsole) {
            // Update Toasts
            double dt = FramePerfMonitor::GetFrameTime();
            int y = 30;
            
            for (auto iter = this->_toasts.rbegin(); iter != this->_toasts.rend(); iter++) {
                iter->timeOnScreen += dt;
                if (iter->timeOnScreen < 5.0) {
                    renderGL->SetColor(0.9, 0.9, 0.9, 0.8);
                    this->_draw->Rect(windowSize.x - 260, y, 250, 16);
                    renderGL->SetColor(0.1, 0.1, 0.1, 1.0);
                    this->_draw->Rect(windowSize.x - 260 + 1, y + 1, 14, 14);
                    renderGL->Print(windowSize.x - 260 + 16, y + 2, iter->domain.c_str());
                    renderGL->SetColor(0.3, 0.3, 0.3, 1.0);
                    renderGL->Print(windowSize.x - 260 + 16 + 80, y + 2, iter->info.c_str());
                    y += 18;
                }
            }
            
            return;
        }
        
        if (this->_currentView == CurrentView::Console) {
            std::vector<Logger::LogEvent>* logEvents = Logger::GetEvents();
            
            bool showVerbose = Config::GetBoolean("core.debug.engineUI.showVerboseLog"); // pretty cheap
            
            int i = windowSize.y - 40;
            
            for (auto iterator = logEvents->rbegin(); iterator < logEvents->rend(); iterator++) {
                if (iterator->Hidden) {
{'fraction_non_alphanumeric': 0.1385395984998897, 'fraction_numerical': 0.030590484594455476, 'mean_word_length': 1.6423159121818536, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 149, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '758655', 'n_tokens_mistral': 4173, 'n_tokens_neox': 3875, 'n_words': 826}