import { Astal, Gdk } from "ags/gtk4";
import app from "ags/gtk4/app"
import Gtk from "gi://Gtk?version=4.0"
import { createBinding, createState } from "ags"
import Tray from "gi://AstalTray";
import { execAsync } from "ags/process"
import Hyprland from "gi://AstalHyprland";
import { getIconName } from "./utils";
import Wp from "gi://AstalWp"
import Battery from "gi://AstalBattery"

const battery = Battery.get_default()
const sensorsAvailable = await execAsync(['sensors']).then(() => true).catch(() => false);
const wirePlumber = Wp.get_default();

function SysTray(): JSX.Element {
    const tray = Tray.get_default();
    return (
        <box>
            {createBinding(tray, "items").as((items) =>
                items.map((item) => {
                    if (item.iconThemePath) app.add_icons(item.iconThemePath);
                    return (
                        <menubutton
                            class="systray"
                            tooltipMarkup={createBinding(item, "tooltipMarkup")}
                            usePopover={false}
                            actionGroup={createBinding(item, "actionGroup").as(ag => ["dbusmenu", ag])}
                            menuModel={createBinding(item, "menuModel")}>
                            {/* <icon gicon={createBinding(item, "gicon")} class="systray-item" /> */}
                        </menubutton>
                    );
                }),
            )}
        </box>
    );
}

function Left(): JSX.Element {
    return (
        <box hexpand halign={Gtk.Align.START}>
            <Clients />
        </box>
    );
}

function Center(): JSX.Element {
    return (
        <box>
            <Workspaces />
        </box>
    );
}

function Date({ format = "%Y-%m-%d" }): JSX.Element {
    const time = createState<string>("").poll(60000, () =>
        GLib.DateTime.new_now_local().format(format)!)
    return <button
        class="item"
        onDestroy={() => time.drop()}
        label={time()}
        onClicked={() => execAsync(['gnome-calendar'])}
    />
}

function Time({ format = "%H:%M:%S" }): JSX.Element {
    const time = createState<string>("").poll(1000, () =>
        GLib.DateTime.new_now_local().format(format)!)
    return <label
        class="item blue"
        onDestroy={() => time.drop()}
        label={time()}
    />
}

function Temp(): JSX.Element {
    let label = createState<string>("N/A");
    if (sensorsAvailable) {
        label = createState<string>("").poll(5000, 'sensors', out => {
            const match = out.split('\n').find(line => line.includes('Tctl') || line.includes('Package'))?.match(/[0-9.]*°C/);
            return match ? match[0] : "N/A";
        })
    }
    return <label
        class="item blue"
        onDestroy={() => label.drop()}
        label={label()}
    />
}

function Memory(): JSX.Element {
    const memory = createState<string>("").poll(2000, "free", out => {
        const line = out.split('\n').find(line => line.includes('Mem:'));
        if (!line) return "N/A";
        const split = line.split(/\s+/).map(Number);
        return (split[2] / 1000000).toFixed(2) + "GB";
    });
    return <label
        class="item blue"
        onDestroy={() => memory.drop()}
        label={memory()}
    />
}

function ClockSpeed(): JSX.Element {
    const command = 'bash -c "cat /proc/cpuinfo | grep \\"MHz\\" | awk \'{print \\$4}\' | sort -n | tail -1 | awk \'{printf \\"%.2fGHz\\", \\$1/1000}\'"';
    const speed = createState<string>("").poll(5000, command)
    return <label
        class="item"
        onDestroy={() => speed.drop()}
        label={speed()}
    />
}

function CPU(): JSX.Element {
    const usage = createState<string>("").poll(2000, "top -b -n 1", out => {
        const line = out.split("\n").find(line => line.includes('Cpu(s)'));
        if (!line) return "N/A";
        return line.split(/\s+/)[1].replace(',', '.').toString() + "%";
    });
    return <box class="item">
        {/* <icon icon="speedometer" css="margin-right: 0.7em;" /> */}
        <label
            onDestroy={() => usage.drop()}
            label={usage()}
        />
    </box>
}



function Right(): JSX.Element {
    return (
        <box class="right" hexpand halign={Gtk.Align.END} spacing={6}>
            <Icons />
            <Volume />
            <CPU />
            <Memory />
            <ClockSpeed />
            <Temp />
            <Date />
            <Time />
        </box>
    );
}

// function BatteryIcon(): JSX.Element {
//     if (battery.get_state() == 0) return <box />;
//     return <button class="battery-item" onClicked={() => execAsync(['gnome-power-statistics'])}>
//         <box>
//             {
//                 createBinding(battery, "percentage").as((percentage) => {
//                     const thresholds = [...Array(11).keys()].map(i => i * 10);
//                     const icon = thresholds.find(threshold => threshold >= percentage * 100)
//                     const charging_name = battery.percentage >= 0.99 ? "charged" : "charging"
//                     return <icon
//                         icon={battery.charging ? `battery-level-${icon}-${charging_name}-symbolic` : `battery-level-${icon}-symbolic`}
//                     />
//                 })
//             }
//         </box>
//     </button>
// }

function Icons() {
    return (
        <box class="item icon-group">
            <SysTray />
            {/* <BatteryIcon /> */}
        </box>
    )
}

function Volume(): JSX.Element {
    if (!wirePlumber) return <box />;

    const audio = wirePlumber.audio;
    const icon = createBinding(audio.default_speaker, "volume").as((volume) => {
        const vol = volume * 100
        const icon = [
            [101, 'overamplified'],
            [67, 'high'],
            [34, 'medium'],
            [1, 'low'],
            [0, 'muted'],
        ].find(([threshold]) => Number(threshold) <= vol)?.[1]
        return `audio-volume-${icon}-symbolic`
    });
    const css = createBinding(audio.default_speaker, "mute").as((mute) => {
        return mute ? "margin-left:0;" : "margin-left: 0.7em;"
    });
    return (
        <button class="item blue" onClicked={() => audio.default_speaker.mute = !audio.default_speaker.mute}>
            <box>
                {/* <icon icon={icon} /> */}
                {
                    createBinding(audio.default_speaker, "volume").as((volume) => <box>
                        {
                            createBinding(audio.default_speaker, "mute").as((mute) => <box>
                                {
                                    <label label={mute ? "" : `${Math.floor(volume * 100)}%`} css={css} />
                                }
                            </box>)
                        }
                    </box>)
                }
            </box>
        </button>
    );
}


function Workspaces(): JSX.Element {
    const hyprland = Hyprland.get_default();
    return (
        <box class="workspaces">
            {createBinding(hyprland, "workspaces").as((wss) =>
                <box>
                    {createBinding(hyprland, "focusedMonitor").as((fm) =>
                        wss.sort((a, b) => a.id - b.id)
                            .filter(ws => ws && ws.get_monitor() && ws.get_monitor().get_id() === fm.get_id())
                            .map((ws) => (
                                <button
                                    class={createBinding(hyprland, "focusedWorkspace").as((fw) => ws === fw ? "focused" : "",)}
                                    onClicked={() => ws.focus()}
                                >
                                    {`${ws.id}`.slice(-1)}
                                </button>
                            )))}
                </box>
            )}
        </box>
    );
}

function shorten(title: string) {
    return title.length > 40 ? title.slice(0, 20) + "..." : title
}

function Clients(): JSX.Element {
    const hyprland = Hyprland.get_default();
    return (
        <box>
            {
                createBinding(hyprland, "focusedWorkspace").as(fw => (
                    <box class="clients">
                        {
                            createBinding(hyprland, "clients").as(cls =>
                                cls
                                    .sort((a, b) => a.pid - b.pid)
                                    .filter(cl => !cl.title.includes("rofi"))
                                    .filter(cl => fw && cl.get_workspace().get_id() === fw.get_id())
                                    .map(cl => (
                                        <box
                                            class={createBinding(hyprland, "focusedClient").as(a => a && a.address === cl.address ? "focused" : "unfocused")}
                                        >
                                            {/* <icon */}
                                            {/*     icon={getIconName(cl)} */}
                                            {/*     class="app-icon" */}
                                            {/* /> */}
                                            <label label={createBinding(cl, 'title').as(title => shorten(title))} />
                                        </box>
                                    )
                                    )
                            )

                        }
                    </box>
                )
                )
            }
        </box>
    );
}

export default function Bar(gdkmonitor: Gdk.Monitor, scaleFactor: number = 1): Gtk.Window {
    return new Gtk.Window({
        gdkmonitor,
        css: "font-size: " + scaleFactor + "em;",
        exclusivity: Astal.Exclusivity.EXCLUSIVE,
        anchor: Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT,
        application: app,
        class: "Bar",
        name: "top-bar",
        s: self => self.connect("destroy", () => {
            print("Detroying bar");
            app.remove_window(self);
        }),
        child: <centerbox class="window-box">
            <Left />
            <Center />
            <Right />
        </centerbox>
    })
}
