Modify to use the default fonts: import ctypes
import math
import re
from typing import Any, Dict, Union, Callable

from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                               QLineEdit, QTextEdit, QScrollBar, QPushButton, QLabel, QFrame,
                               QGridLayout)
from PySide6.QtGui import QFont
from PySide6.QtCore import Qt

#-- Unique Functions --

def capitalize_sentence(sentence: str) -> str:
    """Capitalize the first letter of each word in the sentence."""
    return ' '.join(word.capitalize() for word in sentence.split())

#-- Conversions --

def kg_to_lb(kg: float) -> float:
    """Convert kilograms to pounds."""
    return kg / 0.45359237

def lb_to_kg(lb: float) -> float:
    """Convert pounds to kilograms."""
    return lb * 0.45359237

#-- Processing --

def evaluate_expression(expression: str, use_degrees: bool = False) -> Union[float, str]:
    """Safely evaluate a mathematical expression considering degree mode."""
    
    def convert_angle(func: Callable[[float], float], x: float) -> float:
        return func(math.radians(x)) if use_degrees else func(x)

    def inverse_convert_angle(func: Callable[[float], float], x: float) -> float:
        result = func(x)
        return math.degrees(result) if use_degrees else result

    def safe_eval(expr: str) -> Any:
        safe_globals = {
            "sqrt": math.sqrt,
            "sin": lambda x: convert_angle(math.sin, x),
            "cos": lambda x: convert_angle(math.cos, x),
            "tan": lambda x: convert_angle(math.tan, x),
            "asin": lambda x: inverse_convert_angle(math.asin, x),
            "acos": lambda x: inverse_convert_angle(math.acos, x),
            "atan": lambda x: inverse_convert_angle(math.atan, x),
            "log": math.log10,
            "ln": math.log,
            "pi": math.pi,
            "e": math.e,
            "lb": kg_to_lb,
            "kg": lb_to_kg,
            "capitalize": capitalize_sentence
        }
        return eval(expr, {"__builtins__": None}, safe_globals)

    expression = expression.replace("^", "**")
    expression = re.sub(r'\bcapitalize\(([^\)]+)\)', 
                        lambda match: f'capitalize("{match.group(1)}")', 
                        expression)

    for func, arg in re.findall(r"(\b\w+\b)\(([^)]+)\)", expression):
        match func:
            case "asin" | "acos":
                try:
                    if (value := safe_eval(arg)) < -1 or value > 1:
                        return f"Error: {func}(x) is undefined for |x| > 1"
                except Exception as e:
                    return f"Error: {e}"

    try:
        result = safe_eval(expression)
        if isinstance(result, float) and abs(result) < 1e-10:
            result = 0.0
        return round(result, 2) if isinstance(result, (int, float)) else result
    except Exception as e:
        return f"Error: {e}"


class CalculatorGUI(QMainWindow):
    """PySide6-based Calculator with Degrees/Radians toggle and history."""

    def __init__(self):
        super().__init__()
        self.use_degrees = False
        self.setup_window()
        self.create_widgets()
        self.layout_widgets()

    def setup_window(self) -> None:
        """Configure the main application window."""
        self.setWindowTitle("Calculator")
        self.setGeometry(100, 100, 1280, 720)

    def create_widgets(self) -> None:
        """Initialize all GUI components."""
        self.font = QFont("Roboto Flex", 12)
        self.button_font = QFont("Roboto Flex", 12)
        
        self.entry = QLineEdit()
        self.entry.setFont(self.font)
        self.entry.returnPressed.connect(self.calculate)

        self.history_text = QTextEdit()
        self.history_text.setFont(self.font)
        self.history_text.setReadOnly(True)

        self.scrollbar = QScrollBar(Qt.Vertical)
        self.history_text.setVerticalScrollBar(self.scrollbar)

        self.function_buttons = {
            "Functions": ["sqrt", "sin", "cos", "tan", "asin", "acos", "atan", "log", "ln"],
            "Conversions": ["lb", "kg"],
            "Unique": ["capitalize"],
            "Constants": ["pi", "e"]
        }
        self.function_frame = QFrame()
        self.create_function_buttons()

    def create_function_buttons(self):
        """Create buttons for inserting functions into the entry."""
        layout = QGridLayout()
    
        # Add category labels
        categories = ["Functions", "Conversions", "Unique", "Constants", "Settings"]
        for col, category in enumerate(categories):
            label = QLabel(category, font=self.button_font)
            layout.addWidget(label, 0, col * 3 if category == "Functions" else col + 2)
    
        # Add function buttons
        for col, category in enumerate(categories):
            if category == "Functions":
                for i, func in enumerate(self.function_buttons[category]):
                    button = QPushButton(func)
                    button.setFont(self.button_font)
                    button.clicked.connect(lambda checked, f=func: self.insert_function(f, False))
                    layout.addWidget(button, (i // 3) + 1, i % 3)
            elif category == "Settings":
                # Add Settings button
                self.mode_button = QPushButton("Radians")
                self.mode_button.setFont(self.button_font)
                self.mode_button.clicked.connect(self.toggle_mode)
                layout.addWidget(self.mode_button, 1, col + 2)
            elif category in self.function_buttons:
                column = col + 2  # Adjust for the 3 columns of Functions
                functions = self.function_buttons[category]
                for i, func in enumerate(functions):
                    if category == "Conversions":
                        button_text = f"{func}({'kg' if func == 'lb' else 'lb'})"
                    else:
                        button_text = func
                    is_constant = category == "Constants"
                    button = QPushButton(button_text)
                    button.setFont(self.button_font)
                    button.clicked.connect(lambda checked, f=func, c=is_constant: self.insert_function(f, c))
                    layout.addWidget(button, i + 1, column)
    
        self.function_frame.setLayout(layout)

    def layout_widgets(self) -> None:
        """Arrange widgets within the window."""
        central_widget = QWidget()
        main_layout = QVBoxLayout()
        
        main_layout.addWidget(self.entry)
        
        history_layout = QHBoxLayout()
        history_layout.addWidget(self.history_text)
        history_layout.addWidget(self.scrollbar)
        main_layout.addLayout(history_layout)
        
        main_layout.addWidget(self.function_frame)
        
        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)

    def toggle_mode(self) -> None:
        """Switch between Degrees and Radians modes."""
        self.use_degrees = not self.use_degrees
        self.mode_button.setText("Degrees" if self.use_degrees else "Radians")

    def insert_function(self, function: str, is_constant: bool = False) -> None:
        """Insert function or constant text into the expression entry."""
        current_text = self.entry.text()
        cursor_position = self.entry.cursorPosition()
        
        if is_constant:
            new_text = f"{current_text[:cursor_position]}{function}{current_text[cursor_position:]}"
            new_cursor_position = cursor_position + len(function)
        else:
            new_text = f"{current_text[:cursor_position]}{function}(){current_text[cursor_position:]}"
            new_cursor_position = cursor_position + len(function) + 1
        
        self.entry.setText(new_text)
        self.entry.setCursorPosition(new_cursor_position)
        self.entry.setFocus()

    def calculate(self) -> None:
        """Evaluate the expression and update history."""
        expression = self.entry.text().strip()
        if expression:
            result = evaluate_expression(expression, use_degrees=self.use_degrees)
            self.display_result(expression, result)
            self.entry.clear()

    def display_result(self, expression: str, result: Union[float, str]) -> None:
        """Append the calculation to the history log."""
        mode = "Degrees" if self.use_degrees else "Radians"
        self.history_text.append(f"[{mode}] {expression} = {result}")
        self.history_text.verticalScrollBar().setValue(
            self.history_text.verticalScrollBar().maximum()
        )

def main() -> None:
    """Run the Calculator application."""  
    app = QApplication([])
    calculator = CalculatorGUI()
    calculator.show()
    app.exec()

if __name__ == "__main__":
    main()