bakatui/src/marks.cpp
2025-03-07 15:29:20 +01:00

258 lines
7.3 KiB
C++

#include "marks.h"
#include "helper_funcs.h"
#include "net.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <curses.h>
#include <format>
#include <iostream>
#include <locale>
#include <menu.h>
#include <nlohmann/json.hpp>
#include <panel.h>
#include <string>
using nlohmann::json;
// This code is based on
// https://github.com/tony/NCURSES-Programming-HOWTO-examples/blob/master/16-panels
// MIT License (see original file)
#define NLINES 10
#define NCOLS 40
#define DEFAULT_X_OFFSET 10
#define DEFAULT_Y_OFFSET 2
#define DEFAULT_PADDING 4
void init_wins(WINDOW **wins, int n, json marks_json);
void win_show(WINDOW *win, wchar_t *label, int label_color, int width,
int height, json marks_json, int SubjectIndex);
void marks_page() {
// DONT FORGET TO UNCOMMENT
json resp_from_api = bakaapi::get_data_from_endpoint("api/3/marks");
// std::ifstream f("test-data/marks3.json");
// json resp_from_api = json::parse(f);
WINDOW **my_wins;
size_t size_my_wins = resp_from_api["Subjects"].size();
my_wins = new (std::nothrow) WINDOW *[size_my_wins];
PANEL **my_panels;
size_t size_my_panels = resp_from_api["Subjects"].size();
my_panels = new (std::nothrow) PANEL *[size_my_panels];
PANEL *top;
int ch;
setlocale(LC_ALL, "");
/* Initialize curses */
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
/* Initialize all the colors */
for (uint8_t i = 0; i < 8; i++) {
init_pair(i, i, COLOR_BLACK);
}
init_wins(my_wins, resp_from_api["Subjects"].size(), resp_from_api);
// store all original window position
int *original_y = new int[size_my_wins];
int *original_x = new int[size_my_wins];
for (size_t i = 0; i < size_my_wins; ++i) {
getbegyx(my_wins[i], original_y[i], original_x[i]);
}
// Attach panels
for (size_t i = 0; i < size_my_panels; i++) {
my_panels[i] = new_panel(my_wins[i]);
set_panel_userptr(my_panels[i], (i + 1 < size_my_panels) ? my_panels[i + 1]
: my_panels[0]);
}
update_panels();
attron(COLOR_PAIR(4));
mvprintw(LINES - 2, 0, "Arrows/j/k to scroll | F1 to exit | {mark} [weight]");
attroff(COLOR_PAIR(4));
doupdate();
top = my_panels[size_my_panels - 1];
long y_offset = 0;
// Main loop
while ((ch = getch()) != KEY_F(1)) {
bool needs_update = false;
switch (ch) {
case KEY_UP:
case 'k': // Vim-style up
y_offset--;
needs_update = true;
break;
case KEY_DOWN:
case 'j': // Vim-style down
y_offset++;
needs_update = true;
break;
}
// Update window positions if scrolled
if (needs_update) {
for (size_t i = 0; i < size_my_panels; ++i) {
int new_y = original_y[i] - y_offset;
int new_x = original_x[i];
move_panel(my_panels[i], new_y, new_x);
}
update_panels();
doupdate();
}
}
// Cleanup
endwin();
clear();
delete[] my_wins;
delete[] my_panels;
delete[] original_y;
delete[] original_x;
}
/* Put all the windows */
void init_wins(WINDOW **wins, int n, json marks_json) {
int x, y, i;
wchar_t label[1500];
y = DEFAULT_Y_OFFSET;
x = DEFAULT_X_OFFSET;
uint8_t curent_color = 0;
int MaxHight = 0;
// this loop through subjects
for (i = 0; i < n; ++i) {
// Calculate label and max_text_length to determine window width
std::string sub_name = marks_json["Subjects"][i]["Subject"]["Name"];
std::string sub_avg_s = marks_json["Subjects"][i]["AverageText"];
// Convert to wchar_t
std::wstring wsub_name = string_to_wstring(sub_name);
std::wstring wsub_avg_s = string_to_wstring(sub_avg_s);
// Using swprintf for wide character formatting
swprintf(label, sizeof(label) / sizeof(label[0]), L"%ls - avg: %ls",
wsub_name.c_str(), wsub_avg_s.c_str());
size_t max_text_length = wcslen(label);
for (int j = 0; j < marks_json["Subjects"][i]["Marks"].size(); j++) {
std::string caption =
rm_tr_le_whitespace(marks_json["Subjects"][i]["Marks"][j]["Caption"]);
std::string theme =
rm_tr_le_whitespace(marks_json["Subjects"][i]["Marks"][j]["Theme"]);
std::wstring wcaption = string_to_wstring(caption);
std::wstring wtheme = string_to_wstring(theme);
// Some code that does something and fixes some edge cases
std::string testCaption = caption + std::format(" {{{}}} [{}]", "X", 0);
std::wstring wTestCaption = string_to_wstring(testCaption);
max_text_length =
std::max({max_text_length, wTestCaption.length(), wtheme.length()});
}
int width = max_text_length + DEFAULT_PADDING;
// handle windows overflowing off screen
if (x + width > COLS) {
x = DEFAULT_X_OFFSET;
y += MaxHight + 2;
MaxHight = 0;
}
if (marks_json["Subjects"][i]["Marks"].size() * 2 + DEFAULT_PADDING >
MaxHight) {
MaxHight =
marks_json["Subjects"][i]["Marks"].size() * 2 + DEFAULT_PADDING;
}
wins[i] = newwin(NLINES, NCOLS, y, x);
win_show(wins[i], label, curent_color + 1, width,
marks_json["Subjects"][i]["Marks"].size() * 2 + DEFAULT_PADDING,
marks_json, i);
curent_color = (curent_color + 1) % 7;
x += width + 5;
}
}
/* Show the window with a border and a label */
void win_show(WINDOW *win, wchar_t *label, int label_color, int width,
int height, json marks_json, int SubjectIndex) {
int startx, starty;
wresize(win, height, width);
getbegyx(win, starty, startx);
getmaxyx(win, height, width);
box(win, 0, 0);
mvwaddch(win, 2, 0, ACS_LTEE);
mvwhline(win, 2, 1, ACS_HLINE, width - 2);
mvwaddch(win, 2, width - 1, ACS_RTEE);
wprint_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color));
wchar_t CaptionBuf[1500];
wchar_t ThemeBuf[1500];
std::wstring wCaption;
int AdditionalOffset = 0;
for (size_t i = 0; i < marks_json["Subjects"][SubjectIndex]["Marks"].size();
i++) {
std::string Caption =
marks_json["Subjects"][SubjectIndex]["Marks"][i]["Caption"];
Caption = rm_tr_le_whitespace(Caption);
std::string MarkText =
marks_json["Subjects"][SubjectIndex]["Marks"][i]["MarkText"];
int Weight = marks_json["Subjects"][SubjectIndex]["Marks"][i]["Weight"];
// Create formatted string with mark and weight
std::string formattedCaption =
Caption + std::format(" - {{{}}} [{}]", MarkText, Weight);
// Convert to wide string
wCaption = string_to_wstring(formattedCaption);
wcsncpy(CaptionBuf, wCaption.c_str(),
sizeof(CaptionBuf) / sizeof(CaptionBuf[0]) - 1);
CaptionBuf[sizeof(CaptionBuf) / sizeof(CaptionBuf[0]) - 1] =
L'\0'; // Ensure null termination
wprint_in_middle(win, 3 + i + AdditionalOffset, 0, width, CaptionBuf,
COLOR_PAIR(label_color));
std::string Theme =
marks_json["Subjects"][SubjectIndex]["Marks"][i]["Theme"];
std::wstring wTheme = string_to_wstring(rm_tr_le_whitespace(Theme));
wcsncpy(ThemeBuf, wTheme.c_str(),
sizeof(ThemeBuf) / sizeof(ThemeBuf[0]) - 1);
ThemeBuf[sizeof(ThemeBuf) / sizeof(ThemeBuf[0]) - 1] =
L'\0'; // Ensure null termination
wprint_in_middle(win, 3 + i + 1 + AdditionalOffset, 0, width, ThemeBuf,
COLOR_PAIR(label_color));
AdditionalOffset++;
}
}