/*
    * main.cpp
    * power - CPU power/thermal status tool.
    *
    * Reads the kernel's power management snapshot (SYS_POWERINFO): package
    * temperature, HWP frequency-scaling state, the thermal governor's
    * current frequency ceiling, and the measured average active frequency.
    * Useful for verifying that HWP and the thermal governor are doing their
    * job on real hardware.
    *
    * Usage:
    *   power                 print the current snapshot
    *   power watch           sample once per second until 60 s elapse
    *   power watch <secs>    sample once per second for <secs> seconds
    * Copyright (c) 2026 Daniel Hammer
*/

#include <montauk/syscall.h>

using namespace montauk;

static void put_u64(uint64_t n) {
    char buf[24];
    int i = 0;
    if (n == 0) { putchar('0'); return; }
    while (n) { buf[i++] = (char)('0' + (n % 10)); n /= 10; }
    while (i) putchar(buf[--i]);
}

static void put_hex8(uint8_t v) {
    static const char* digits = "0123456789ABCDEF";
    print("0x");
    putchar(digits[(v >> 4) & 0xF]);
    putchar(digits[v & 0xF]);
}

static bool read_info(montauk::abi::PowerInfo& info) {
    return syscall1(montauk::abi::SYS_POWERINFO, (uint64_t)&info) == 0;
}

static void print_snapshot(const montauk::abi::PowerInfo& info) {
    print("CPU power/thermal status:\n");

    print("  HWP:        ");
    if (info.hwpActive) {
        print("active (EPP ");
        put_hex8(info.epp);
        print(", perf range ");
        put_u64(info.lowestPerf);
        print("-");
        put_u64(info.highestPerf);
        print(")\n");
    } else {
        print("not active\n");
    }

    print("  Frequency:  base ");
    put_u64(info.baseMHz);
    print(" MHz, turbo ");
    put_u64(info.maxMHz);
    print(" MHz, avg active ");
    put_u64(info.effMHz);
    print(" MHz\n");

    print("  Package:    ");
    if (info.tempC != 0) {
        put_u64(info.tempC);
        print(" C (hardware throttles at ");
        put_u64(info.tjMaxC);
        print(" C)\n");
    } else {
        print("temperature unavailable\n");
    }

    print("  Governor:   ");
    if (!info.hwpActive) {
        print("inactive\n");
    } else if (info.throttling) {
        print("THROTTLING - ceiling ");
        put_u64(info.curMaxPerf);
        print(" of ");
        put_u64(info.highestPerf);
        print("\n");
    } else {
        print("not throttling (ceiling ");
        put_u64(info.curMaxPerf);
        print(")\n");
    }

    print("  AP idle:    MWAIT hint ");
    put_hex8((uint8_t)info.apIdleHint);
    if (info.apIdleHint == 0) {
        print(" (C1 only)");
    }
    print("\n");
}

static void print_watch_line(const montauk::abi::PowerInfo& info, uint64_t t) {
    put_u64(t);
    print("s  ");
    put_u64(info.tempC);
    print(" C  avg ");
    put_u64(info.effMHz);
    print(" MHz  ceiling ");
    put_u64(info.curMaxPerf);
    print("/");
    put_u64(info.highestPerf);
    print(info.throttling ? "  THROTTLING\n" : "\n");
}

static uint64_t parse_u64(const char* s, uint64_t fallback) {
    uint64_t v = 0;
    bool any = false;
    while (*s == ' ') s++;
    while (*s >= '0' && *s <= '9') { v = v * 10 + (uint64_t)(*s - '0'); s++; any = true; }
    return any ? v : fallback;
}

extern "C" void _start() {
    char args[64];
    int alen = montauk::getargs(args, sizeof(args));
    const char* rest = (alen > 0) ? args : "";

    montauk::abi::PowerInfo info{};
    if (!read_info(info)) {
        print("power: no CPU power management available\n");
        print("(non-Intel CPU or running under emulation)\n");
        montauk::exit(1);
    }

    bool watch = rest[0] == 'w';
    if (!watch) {
        print_snapshot(info);
        montauk::exit(0);
    }

    const char* p = rest;
    while (*p && *p != ' ') p++;
    uint64_t seconds = parse_u64(p, 60);
    if (seconds == 0) seconds = 60;

    print("time  temp  freq  ceiling (1 sample/s, ");
    put_u64(seconds);
    print(" s)\n");

    for (uint64_t t = 0; t < seconds; t++) {
        if (!read_info(info)) break;
        print_watch_line(info, t);
        montauk::sleep_ms(1000);
    }
    montauk::exit(0);
}
