#!/usr/bin/env python
import json
import shutil
import subprocess
import sys
import typing

__all__ = ["_mem", "_backlight", "_hwtemp"]

_BITS: list[tuple[int, str, typing.Callable]] = []


def _bit(idx):
    def wrapper(func):
        _BITS.append((idx, func.__name__.replace("_", ""), func))
        return func

    return wrapper


@_bit(6)
def _mem():
    with open("/proc/meminfo") as meminfo_fp:
        attrs = {}
        for line in meminfo_fp.readlines():
            parts = line.split(":", 2)
            attrs[parts[0].strip()] = parts[1].strip()
        avail = int(attrs["MemAvailable"].split()[0])
        total = int(attrs["MemTotal"].split()[0])
        used = int(((total - avail) / total) * 100)
        color = None
        if used > 75:
            color = "#ff0000"
        if used < 25:
            color = "#00ff00"
        return "M:{}%".format(used), color


@_bit(0)
def _backlight(dev_dir="/sys/class/backlight/intel_backlight"):
    with open("{}/brightness".format(dev_dir)) as br_fp:
        with open("{}/max_brightness".format(dev_dir)) as mb_fp:
            value = int(br_fp.read().strip())
            max_value = int(mb_fp.read().strip())
            pct = int(float(float(value) / float(max_value)) * 100)
            color = None
            if pct >= 75:
                color = "#ffaa00"
            if pct <= 25:
                color = "#5599ff"
            return "☼:{}%".format(pct), color


ACPI_IBM_FAN = "/proc/acpi/ibm/fan"


@_bit(7)
def _hwtemp(fan=ACPI_IBM_FAN):
    acpi_exe = shutil.which("acpi")
    if acpi_exe is None:
        return None, None

    raw_temp = subprocess.run(
        [acpi_exe, "-t"],
        text=True,
        capture_output=True,
        check=False,
    )
    if raw_temp.returncode != 0 or raw_temp.stdout is None:
        return None, None

    temps_by_zone = {}
    for line in raw_temp.stdout.splitlines():
        zone_id, value = line.split(":")
        human_temp = value.split(",")[-1]
        temps_by_zone[zone_id.lower().lstrip("thermal ")] = float(
            human_temp.lower().strip().rstrip("degrees c")
        )

    temps = list(temps_by_zone.values())
    avg_temp = float(sum(temps)) / len(temps)
    color = "#5599ff"
    if avg_temp > 75:
        color = "#ff0000"
    if avg_temp >= 50:
        color = "#ffaa00"

    fan_level = "unset"
    try:
        with open(fan) as fan_fp:
            for line in fan_fp.readlines():
                if not line.startswith("level:\t\t"):
                    continue
                fan_level = int(line.replace("level:\t\t", ""))
    except (OSError, IOError) as exc:
        sys.stderr.write(str(exc) + "\n")

    return "T:{:.1f}°C L:{}".format(avg_temp, fan_level), color


def _print_line(message):
    sys.stdout.write(message + "\n")
    sys.stdout.flush()


def _read_line():
    try:
        line = sys.stdin.readline().strip()
        if not line:
            sys.exit(3)
        return line
    except KeyboardInterrupt:
        sys.exit()


def main(bits=_BITS):
    _print_line(_read_line())
    _print_line(_read_line())

    while True:
        line, prefix = _read_line(), ""
        if line.startswith(","):
            line, prefix = line[1:], ","

        loaded = json.loads(line)
        for idx, name, func in bits:
            try:
                value, color = func()
                if value is None:
                    continue

                record = dict(full_text=str(value), name=name)
                if color is not None:
                    record.update(dict(color=color))

                loaded.insert(idx, record)
            except Exception as exc:
                sys.stderr.write(str(exc) + "\n")
                sys.stderr.flush()

        _print_line(prefix + json.dumps(loaded))


if __name__ == "__main__":
    main()
