const std = @import("std");

const Mutex = @import("mutex.zig").Mutex;

const log = std.log.scoped(.command);

pub const Command = struct {
    key: u8,
    label: []const u8,
    command: [][]const u8,
    exit: bool,

    pub fn deinit(self: Command, alloc: std.mem.Allocator) void {
        alloc.free(self.command);
    }

    pub fn run(
        self: Command,
        alloc: std.mem.Allocator,
        exit: *@import("util.zig").ExitMode,
        env: *Mutex(std.process.EnvMap),
    ) !void {
        if (std.mem.eql(u8, self.command[0], "!quit")) {
            exit.* = .delayed;
            log.info("user logged out", .{});
            return;
        }

        if (self.exit) exit.* = .immediate;

        log.info("run cmd: {s}", .{self.command});
        var child = std.ChildProcess.init(self.command, alloc);
        {
            env.mtx.lock();
            defer env.mtx.unlock();
            child.env_map = &env.data;
            try child.spawn();
        }
        _ = try child.wait();
    }
};

pub fn parseEntriesConfig(alloc: std.mem.Allocator, data: []const u8) ![]Command {
    var entries = std.ArrayList(Command).init(alloc);
    errdefer entries.deinit();

    var line_splits = std.mem.tokenizeScalar(u8, data, '\n');
    while (line_splits.next()) |line| {
        const line_without_comment = std.mem.sliceTo(line, '#');
        if (line_without_comment.len == 0)
            continue;

        var seg_splits = std.mem.tokenizeScalar(u8, line_without_comment, ':');
        const labels = std.mem.trim(u8, seg_splits.next() orelse return error.InvalidConfig, &std.ascii.whitespace);
        const command = std.mem.trim(u8, seg_splits.next() orelse return error.InvalidConfig, &std.ascii.whitespace);
        var exit = false;
        if (seg_splits.next()) |extra| {
            if (std.mem.eql(u8, extra, "Q")) {
                exit = true;
            } else return error.InvalidConfig;
        }
        if (seg_splits.next()) |_| return error.InvalidConfig;

        if (labels.len < 3 or labels[1] != ' ') return error.InvalidConfig;
        const key = std.ascii.toUpper(labels[0]);
        const label = labels[2..];

        var argv = std.ArrayList([]const u8).init(alloc);
        errdefer argv.deinit();
        var command_splits = std.mem.splitScalar(u8, command, ',');
        while (command_splits.next()) |arg|
            try argv.append(arg);

        if (argv.items.len == 0) return error.InvalidConfig;

        try entries.append(.{
            .key = key,
            .label = label,
            .command = try argv.toOwnedSlice(),
            .exit = exit,
        });
    }

    return try entries.toOwnedSlice();
}
