From fd6a14675beacd6adbc79c1d51cc05aaa274a070 Mon Sep 17 00:00:00 2001
From: Grayson Riffe <grayson@graysonriffe.com>
Date: Mon, 20 Jan 2025 23:06:16 -0600
Subject: [PATCH] Basic timing

---
 Mainspring/resource.rc         |  8 ++---
 Mainspring/src/Application.cpp | 61 +++++++++++++++++++++++++++++++---
 Mainspring/src/Application.h   | 13 ++++++--
 3 files changed, 71 insertions(+), 11 deletions(-)

diff --git a/Mainspring/resource.rc b/Mainspring/resource.rc
index d6ebe32..fae0d6f 100644
--- a/Mainspring/resource.rc
+++ b/Mainspring/resource.rc
@@ -20,12 +20,12 @@ BEGIN
     END
 END
 
-IDD_DIALOGMAIN DIALOGEX 0, 0, 175, 75
+IDD_DIALOGMAIN DIALOGEX 0, 0, 175, 70
 STYLE WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_SETFONT
 FONT 8, "MS Shell Dlg", 400, 0, 0x1
 MENU IDR_MENUMAIN
 BEGIN
-    CTEXT "APPNAME", IDC_STATICTITLE, 30, 5, 115, 20, SS_CENTERIMAGE
-    CTEXT "TIME", IDC_STATICTIME, 10, 25, 155, 25, SS_CENTERIMAGE
-    DEFPUSHBUTTON "STARTPAUSE" IDC_BUTTONSTARTPAUSE, 63, 55, 50, 15
+    CTEXT "APPNAME", IDC_STATICTITLE, 30, 0, 115, 20, SS_CENTERIMAGE
+    CTEXT "TIME", IDC_STATICTIME, 0, 20, 175, 25, SS_CENTERIMAGE
+    DEFPUSHBUTTON "STARTPAUSE" IDC_BUTTONSTARTPAUSE, 63, 50, 50, 15
 END
\ No newline at end of file
diff --git a/Mainspring/src/Application.cpp b/Mainspring/src/Application.cpp
index 673dbd8..73c8b75 100644
--- a/Mainspring/src/Application.cpp
+++ b/Mainspring/src/Application.cpp
@@ -3,6 +3,9 @@
 #include "Application.h"
 #include "resources.h"
 
+// Define window message to update the timer display
+#define WM_UPDATETIME WM_USER
+
 // Enable visual styles
 #pragma comment(linker, "\"/manifestdependency:type='win32' \
 name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
@@ -12,11 +15,15 @@ namespace mainspring {
     Application::Application(const char* appName, const char* appVersion)
         : m_appName(appName)
         , m_appVersion(appVersion)
+        , m_timing(false)
+        , m_elapsed()
     {
         std::cout << std::format("{} {}\n", m_appName, m_appVersion);
     }
 
     void Application::run() {
+        // Read last time here
+
         DialogBoxParam(nullptr, MAKEINTRESOURCE(IDD_DIALOGMAIN), nullptr, reinterpret_cast<DLGPROC>(mainDlgProc), reinterpret_cast<LPARAM>(this));
     }
 
@@ -42,24 +49,68 @@ namespace mainspring {
                 HFONT titleFont = CreateFontIndirect(&lFont);
                 SendMessage(GetDlgItem(dlg, IDC_STATICTITLE), WM_SETFONT, reinterpret_cast<WPARAM>(titleFont), NULL);
 
-                lFont.lfHeight = 40;
+                std::memcpy(lFont.lfFaceName, "Consolas", 9);
+                lFont.lfHeight = 60;
                 HFONT timeFont = CreateFontIndirect(&lFont);
                 SendMessage(GetDlgItem(dlg, IDC_STATICTIME), WM_SETFONT, reinterpret_cast<WPARAM>(timeFont), NULL);
 
                 SetDlgItemText(dlg, IDC_BUTTONSTARTPAUSE, "Start");
 
+                SendMessage(dlg, WM_UPDATETIME, NULL, NULL);
+
+                return TRUE;
+            }
+
+            case WM_COMMAND:
+                switch (LOWORD(wParam)) {
+                    case IDC_BUTTONSTARTPAUSE: {
+                        app->m_timing = !app->m_timing;
+
+                        if (app->m_timing) {
+                            SetDlgItemText(dlg, IDC_BUTTONSTARTPAUSE, "Pause");
+                            app->m_startTime = Clock::now();
+                            // Timer to update time
+                            SetTimer(dlg, 1, 50, nullptr);
+                        }
+                        else {
+                            SetDlgItemText(dlg, IDC_BUTTONSTARTPAUSE, "Start");
+                            KillTimer(dlg, 1);
+                        }
+
+                        return TRUE;
+                    }
+
+                    case ID_HELP_ABOUT:
+                        MessageBox(dlg, std::format("{} {}\nCopyright Grayson Riffe 2025\ngraysonriffe.com", app->m_appName, app->m_appVersion).c_str(), std::format("About {}", app->m_appName).c_str(), MB_OK);
+                        return TRUE;
+                }
+
+                return FALSE;
+
+            case WM_TIMER: {
+                SendMessage(dlg, WM_UPDATETIME, NULL, NULL);
+                return TRUE;
+            }
+
+            case WM_UPDATETIME: {
+                Seconds sinceLastStart = 0;
+                if(app->m_timing)
+                    sinceLastStart = Duration(Clock::now() - app->m_startTime).count();
+
+                Seconds totalTime = app->m_elapsed + sinceLastStart;
+
+                uint32_t hr = totalTime / (3600), min = totalTime % (3600) / 60, sec = totalTime % 60;
+                SetDlgItemText(dlg, IDC_STATICTIME, std::format("{:02}:{:02}:{:02}", hr, min, sec).c_str());
+
                 return TRUE;
             }
 
             case WM_CLOSE:
+                // TODO: Ask to / save time here
                 EndDialog(dlg, 0);
                 return TRUE;
         }
 
         return FALSE;
     }
-
-    Application::~Application() {
-
-    }
 }
\ No newline at end of file
diff --git a/Mainspring/src/Application.h b/Mainspring/src/Application.h
index 6da9a98..c73c7f3 100644
--- a/Mainspring/src/Application.h
+++ b/Mainspring/src/Application.h
@@ -1,17 +1,26 @@
 // Application class header
 
 namespace mainspring {
+    using Clock = std::chrono::high_resolution_clock;
+    using TimePoint = std::chrono::time_point<Clock>;
+    using Duration = std::chrono::duration<double, std::ratio<1, 1>>;
+
+    using Seconds = uint64_t;
+
     class Application {
     public:
         Application(const char* appName, const char* appVersion);
 
         void run();
-
-        ~Application();
     private:
         static BOOL CALLBACK mainDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam);
 
         const char* m_appName;
         const char* m_appVersion;
+
+        // Current "Time" (state of the app)
+        bool m_timing; // Currently counting time?
+        Seconds m_elapsed; // in seconds
+        TimePoint m_startTime; // When the timer was started
     };
 }
\ No newline at end of file