const std = @import("std");

const Parser = @import("../../osc.zig").Parser;
const Command = @import("../../osc.zig").Command;

/// Parse OSC 9, which could be an iTerm2 notification or a ConEmu extension.
pub fn parse(parser: *Parser, _: ?u8) ?*Command {
    const cap = if (parser.capture) |*c| c else {
        parser.state = .invalid;
        return null;
    };
    const writer = cap.writer;

    // Check first to see if this is a ConEmu OSC
    // https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC
    conemu: {
        var data = cap.trailing();
        if (data.len == 0) break :conemu;
        switch (data[0]) {
            // Check for OSC 9;1 9;10 9;11 9;12
            '1' => {
                if (data.len < 2) break :conemu;
                switch (data[1]) {
                    // OSC 9;1 sleep
                    ';' => {
                        parser.command = .{
                            .conemu_sleep = .{
                                .duration_ms = if (std.fmt.parseUnsigned(u16, data[2..], 10)) |num| @min(num, 10_000) else |_| 100,
                            },
                        };
                        return &parser.command;
                    },
                    // OSC 9;10 xterm keyboard and output emulation
                    '0' => {
                        if (data.len == 2) {
                            parser.command = .{
                                .conemu_xterm_emulation = .{
                                    .keyboard = true,
                                    .output = true,
                                },
                            };
                            return &parser.command;
                        }
                        if (data.len < 4) break :conemu;
                        if (data[2] != ';') break :conemu;
                        switch (data[3]) {
                            '0' => {
                                parser.command = .{
                                    .conemu_xterm_emulation = .{
                                        .keyboard = false,
                                        .output = false,
                                    },
                                };
                                return &parser.command;
                            },
                            '1' => {
                                parser.command = .{
                                    .conemu_xterm_emulation = .{
                                        .keyboard = true,
                                        .output = true,
                                    },
                                };
                                return &parser.command;
                            },
                            '2' => {
                                parser.command = .{
                                    .conemu_xterm_emulation = .{
                                        .keyboard = null,
                                        .output = false,
                                    },
                                };
                                return &parser.command;
                            },
                            '3' => {
                                parser.command = .{
                                    .conemu_xterm_emulation = .{
                                        .keyboard = null,
                                        .output = true,
                                    },
                                };
                                return &parser.command;
                            },
                            else => break :conemu,
                        }
                    },
                    // OSC 9;11 comment
                    '1' => {
                        if (data.len < 3) break :conemu;
                        if (data[2] != ';') break :conemu;
                        writer.writeByte(0) catch {
                            parser.state = .invalid;
                            return null;
                        };
                        data = cap.trailing();
                        parser.command = .{
                            .conemu_comment = data[3 .. data.len - 1 :0],
                        };
                        return &parser.command;
                    },
                    // OSC 9;12 mark prompt start
                    '2' => {
                        parser.command = .{ .semantic_prompt = .init(.fresh_line_new_prompt) };
                        return &parser.command;
                    },
                    else => break :conemu,
                }
            },
            // OSC 9;2 show message box
            '2' => {
                if (data.len < 2) break :conemu;
                if (data[1] != ';') break :conemu;
                writer.writeByte(0) catch {
                    parser.state = .invalid;
                    return null;
                };
                data = cap.trailing();
                parser.command = .{
                    .conemu_show_message_box = data[2 .. data.len - 1 :0],
                };
                return &parser.command;
            },
            // OSC 9;3 change tab title
            '3' => {
                if (data.len < 2) break :conemu;
                if (data[1] != ';') break :conemu;
                if (data.len == 2) {
                    parser.command = .{
                        .conemu_change_tab_title = .reset,
                    };
                    return &parser.command;
                }
                writer.writeByte(0) catch {
                    parser.state = .invalid;
                    return null;
                };
                data = cap.trailing();
                parser.command = .{
                    .conemu_change_tab_title = .{
                        .value = data[2 .. data.len - 1 :0],
                    },
                };
                return &parser.command;
            },
            // OSC 9;4 progress report
            '4' => {
                if (data.len < 2) break :conemu;
                if (data[1] != ';') break :conemu;
                if (data.len < 3) break :conemu;
                switch (data[2]) {
                    '0' => {
                        parser.command = .{
                            .conemu_progress_report = .{
                                .state = .remove,
                            },
                        };
                    },
                    '1' => {
                        parser.command = .{
                            .conemu_progress_report = .{
                                .state = .set,
                                .progress = 0,
                            },
                        };
                    },
                    '2' => {
                        parser.command = .{
                            .conemu_progress_report = .{
                                .state = .@"error",
                            },
                        };
                    },
                    '3' => {
                        parser.command = .{
                            .conemu_progress_report = .{
                                .state = .indeterminate,
                            },
                        };
                    },
                    '4' => {
                        parser.command = .{
                            .conemu_progress_report = .{
                                .state = .pause,
                            },
                        };
                    },
                    else => break :conemu,
                }
                switch (parser.command.conemu_progress_report.state) {
                    .remove, .indeterminate => {},
                    .set, .@"error", .pause => progress: {
                        if (data.len < 4) break :progress;
                        if (data[3] != ';') break :progress;
                        // parse the progress value
                        parser.command.conemu_progress_report.progress = value: {
                            break :value @intCast(std.math.clamp(
                                std.fmt.parseUnsigned(usize, data[4..], 10) catch break :value null,
                                0,
                                100,
                            ));
                        };
                    },
                }
                return &parser.command;
            },
            // OSC 9;5 wait for input
            '5' => {
                parser.command = .conemu_wait_input;
                return &parser.command;
            },
            // OSC 9;6 guimacro
            '6' => {
                if (data.len < 2) break :conemu;
                if (data[1] != ';') break :conemu;
                writer.writeByte(0) catch {
                    parser.state = .invalid;
                    return null;
                };
                data = cap.trailing();
                parser.command = .{
                    .conemu_guimacro = data[2 .. data.len - 1 :0],
                };
                return &parser.command;
            },
            // OSC 9;7 run process
            '7' => {
                if (data.len < 2) break :conemu;
                if (data[1] != ';') break :conemu;
                writer.writeByte(0) catch {
                    parser.state = .invalid;
                    return null;
                };
                data = cap.trailing();
                parser.command = .{
                    .conemu_run_process = data[2 .. data.len - 1 :0],
                };
                return &parser.command;
            },
            // OSC 9;8 output environment variable
            '8' => {
                if (data.len < 2) break :conemu;
                if (data[1] != ';') break :conemu;
                writer.writeByte(0) catch {
                    parser.state = .invalid;
                    return null;
                };
                data = cap.trailing();
                parser.command = .{
                    .conemu_output_environment_variable = data[2 .. data.len - 1 :0],
                };
                return &parser.command;
            },
            // OSC 9;9 current working directory
            '9' => {
                if (data.len < 2) break :conemu;
                if (data[1] != ';') break :conemu;
                writer.writeByte(0) catch {
                    parser.state = .invalid;
                    return null;
                };
                data = cap.trailing();
                parser.command = .{
                    .report_pwd = .{
                        .value = data[2 .. data.len - 1 :0],
                    },
                };
                return &parser.command;
            },
            else => break :conemu,
        }
    }

    // If it's not a ConEmu OSC, it's an iTerm2 notification

    writer.writeByte(0) catch {
        parser.state = .invalid;
        return null;
    };
    const data = cap.trailing();
    parser.command = .{
        .show_desktop_notification = .{
            .title = "",
            .body = data[0 .. data.len - 1 :0],
        },
    };
    return &parser.command;
}

test "OSC 9: show desktop notification" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;Hello world";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("", cmd.show_desktop_notification.title);
    try testing.expectEqualStrings("Hello world", cmd.show_desktop_notification.body);
}

test "OSC 9: show single character desktop notification" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;H";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("", cmd.show_desktop_notification.title);
    try testing.expectEqualStrings("H", cmd.show_desktop_notification.body);
}

test "OSC 9;1: ConEmu sleep" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;1;420";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .conemu_sleep);
    try testing.expectEqual(420, cmd.conemu_sleep.duration_ms);
}

test "OSC 9;1: ConEmu sleep with no value default to 100ms" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;1;";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .conemu_sleep);
    try testing.expectEqual(100, cmd.conemu_sleep.duration_ms);
}

test "OSC 9;1: conemu sleep cannot exceed 10000ms" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;1;12345";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .conemu_sleep);
    try testing.expectEqual(10000, cmd.conemu_sleep.duration_ms);
}

test "OSC 9;1: conemu sleep invalid input" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;1;foo";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .conemu_sleep);
    try testing.expectEqual(100, cmd.conemu_sleep.duration_ms);
}

test "OSC 9;1: conemu sleep -> desktop notification 1" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;1";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("1", cmd.show_desktop_notification.body);
}

test "OSC 9;1: conemu sleep -> desktop notification 2" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;1a";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("1a", cmd.show_desktop_notification.body);
}

test "OSC 9;2: ConEmu message box" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;2;hello world";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_show_message_box);
    try testing.expectEqualStrings("hello world", cmd.conemu_show_message_box);
}

test "OSC 9;2: ConEmu message box invalid input" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;2";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("2", cmd.show_desktop_notification.body);
}

test "OSC 9;2: ConEmu message box empty message" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;2;";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_show_message_box);
    try testing.expectEqualStrings("", cmd.conemu_show_message_box);
}

test "OSC 9;2: ConEmu message box spaces only message" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;2;   ";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_show_message_box);
    try testing.expectEqualStrings("   ", cmd.conemu_show_message_box);
}

test "OSC 9;2: message box -> desktop notification 1" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;2";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("2", cmd.show_desktop_notification.body);
}

test "OSC 9;2: message box -> desktop notification 2" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;2a";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("2a", cmd.show_desktop_notification.body);
}

test "OSC 9;3: ConEmu change tab title" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;3;foo bar";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_change_tab_title);
    try testing.expectEqualStrings("foo bar", cmd.conemu_change_tab_title.value);
}

test "OSC 9;3: ConEmu change tab title reset" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;3;";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    const expected_command: Command = .{ .conemu_change_tab_title = .reset };
    try testing.expectEqual(expected_command, cmd);
}

test "OSC 9;3: ConEmu change tab title spaces only" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;3;   ";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .conemu_change_tab_title);
    try testing.expectEqualStrings("   ", cmd.conemu_change_tab_title.value);
}

test "OSC 9;3: change tab title -> desktop notification 1" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;3";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("3", cmd.show_desktop_notification.body);
}

test "OSC 9;3: message box -> desktop notification 2" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;3a";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("3a", cmd.show_desktop_notification.body);
}

test "OSC 9;4: ConEmu progress set" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;1;100";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_progress_report);
    try testing.expect(cmd.conemu_progress_report.state == .set);
    try testing.expect(cmd.conemu_progress_report.progress == 100);
}

test "OSC 9;4: ConEmu progress set overflow" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;1;900";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_progress_report);
    try testing.expect(cmd.conemu_progress_report.state == .set);
    try testing.expectEqual(100, cmd.conemu_progress_report.progress);
}

test "OSC 9;4: ConEmu progress set single digit" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;1;9";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_progress_report);
    try testing.expect(cmd.conemu_progress_report.state == .set);
    try testing.expect(cmd.conemu_progress_report.progress == 9);
}

test "OSC 9;4: ConEmu progress set double digit" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;1;94";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_progress_report);
    try testing.expect(cmd.conemu_progress_report.state == .set);
    try testing.expectEqual(94, cmd.conemu_progress_report.progress);
}

test "OSC 9;4: ConEmu progress set extra semicolon ignored" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;1;100";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_progress_report);
    try testing.expect(cmd.conemu_progress_report.state == .set);
    try testing.expectEqual(100, cmd.conemu_progress_report.progress);
}

test "OSC 9;4: ConEmu progress remove with no progress" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;0;";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_progress_report);
    try testing.expect(cmd.conemu_progress_report.state == .remove);
    try testing.expect(cmd.conemu_progress_report.progress == null);
}

test "OSC 9;4: ConEmu progress remove with double semicolon" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;0;;";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_progress_report);
    try testing.expect(cmd.conemu_progress_report.state == .remove);
    try testing.expect(cmd.conemu_progress_report.progress == null);
}

test "OSC 9;4: ConEmu progress remove ignores progress" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;0;100";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_progress_report);
    try testing.expect(cmd.conemu_progress_report.state == .remove);
    try testing.expect(cmd.conemu_progress_report.progress == null);
}

test "OSC 9;4: ConEmu progress remove extra semicolon" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;0;100;";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_progress_report);
    try testing.expect(cmd.conemu_progress_report.state == .remove);
}

test "OSC 9;4: ConEmu progress error" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;2";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_progress_report);
    try testing.expect(cmd.conemu_progress_report.state == .@"error");
    try testing.expect(cmd.conemu_progress_report.progress == null);
}

test "OSC 9;4: ConEmu progress error with progress" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;2;100";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_progress_report);
    try testing.expect(cmd.conemu_progress_report.state == .@"error");
    try testing.expect(cmd.conemu_progress_report.progress == 100);
}

test "OSC 9;4: progress pause" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;4";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_progress_report);
    try testing.expect(cmd.conemu_progress_report.state == .pause);
    try testing.expect(cmd.conemu_progress_report.progress == null);
}

test "OSC 9;4: ConEmu progress pause with progress" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;4;100";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_progress_report);
    try testing.expect(cmd.conemu_progress_report.state == .pause);
    try testing.expect(cmd.conemu_progress_report.progress == 100);
}

test "OSC 9;4: progress -> desktop notification 1" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("4", cmd.show_desktop_notification.body);
}

test "OSC 9;4: progress -> desktop notification 2" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("4;", cmd.show_desktop_notification.body);
}

test "OSC 9;4: progress -> desktop notification 3" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;5";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("4;5", cmd.show_desktop_notification.body);
}

test "OSC 9;4: progress -> desktop notification 4" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;4;5a";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;

    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("4;5a", cmd.show_desktop_notification.body);
}

test "OSC 9;5: ConEmu wait input" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;5";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_wait_input);
}

test "OSC 9;5: ConEmu wait ignores trailing characters" {
    const testing = std.testing;

    var p: Parser = .init(null);

    const input = "9;5;foo";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_wait_input);
}

test "OSC 9;6: ConEmu guimacro 1" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;6;a";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_guimacro);
    try testing.expectEqualStrings("a", cmd.conemu_guimacro);
}

test "OSC: 9;6: ConEmu guimacro 2" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;6;ab";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_guimacro);
    try testing.expectEqualStrings("ab", cmd.conemu_guimacro);
}

test "OSC: 9;6: ConEmu guimacro 3 incomplete -> desktop notification" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;6";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("6", cmd.show_desktop_notification.body);
}

test "OSC: 9;7: ConEmu run process 1" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;7;ab";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_run_process);
    try testing.expectEqualStrings("ab", cmd.conemu_run_process);
}

test "OSC: 9;7: ConEmu run process 2" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;7;";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_run_process);
    try testing.expectEqualStrings("", cmd.conemu_run_process);
}

test "OSC: 9;7: ConEmu run process incomplete -> desktop notification" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;7";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("7", cmd.show_desktop_notification.body);
}

test "OSC: 9;8: ConEmu output environment variable 1" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;8;ab";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_output_environment_variable);
    try testing.expectEqualStrings("ab", cmd.conemu_output_environment_variable);
}

test "OSC: 9;8: ConEmu output environment variable 2" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;8;";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_output_environment_variable);
    try testing.expectEqualStrings("", cmd.conemu_output_environment_variable);
}

test "OSC: 9;8: ConEmu output environment variable incomplete -> desktop notification" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;8";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("8", cmd.show_desktop_notification.body);
}

test "OSC: 9;9: ConEmu set current working directory" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;9;ab";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .report_pwd);
    try testing.expectEqualStrings("ab", cmd.report_pwd.value);
}

test "OSC: 9;9: ConEmu set current working directory incomplete -> desktop notification" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;9";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("9", cmd.show_desktop_notification.body);
}

test "OSC: 9;10: ConEmu xterm keyboard and output emulation 1" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;10";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_xterm_emulation);
    try testing.expect(cmd.conemu_xterm_emulation.keyboard != null);
    try testing.expect(cmd.conemu_xterm_emulation.keyboard.? == true);
    try testing.expect(cmd.conemu_xterm_emulation.output != null);
    try testing.expect(cmd.conemu_xterm_emulation.output.? == true);
}

test "OSC: 9;10: ConEmu xterm keyboard and output emulation 2" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;10;0";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_xterm_emulation);
    try testing.expect(cmd.conemu_xterm_emulation.keyboard != null);
    try testing.expect(cmd.conemu_xterm_emulation.keyboard.? == false);
    try testing.expect(cmd.conemu_xterm_emulation.output != null);
    try testing.expect(cmd.conemu_xterm_emulation.output.? == false);
}

test "OSC: 9;10: ConEmu xterm keyboard and output emulation 3" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;10;1";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_xterm_emulation);
    try testing.expect(cmd.conemu_xterm_emulation.keyboard != null);
    try testing.expect(cmd.conemu_xterm_emulation.keyboard.? == true);
    try testing.expect(cmd.conemu_xterm_emulation.output != null);
    try testing.expect(cmd.conemu_xterm_emulation.output.? == true);
}

test "OSC: 9;10: ConEmu xterm keyboard and output emulation 4" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;10;2";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_xterm_emulation);
    try testing.expect(cmd.conemu_xterm_emulation.keyboard == null);
    try testing.expect(cmd.conemu_xterm_emulation.output != null);
    try testing.expect(cmd.conemu_xterm_emulation.output.? == false);
}

test "OSC: 9;10: ConEmu xterm keyboard and output emulation 5" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;10;3";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_xterm_emulation);
    try testing.expect(cmd.conemu_xterm_emulation.keyboard == null);
    try testing.expect(cmd.conemu_xterm_emulation.output != null);
    try testing.expect(cmd.conemu_xterm_emulation.output.? == true);
}

test "OSC: 9;10: ConEmu xterm keyboard and output emulation 6" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;10;4";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("10;4", cmd.show_desktop_notification.body);
}

test "OSC: 9;10: ConEmu xterm keyboard and output emulation 7" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;10;";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("10;", cmd.show_desktop_notification.body);
}

test "OSC: 9;10: ConEmu xterm keyboard and output emulation 8" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;10;abc";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("10;abc", cmd.show_desktop_notification.body);
}

test "OSC: 9;11: ConEmu comment" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;11;ab";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .conemu_comment);
    try testing.expectEqualStrings("ab", cmd.conemu_comment);
}

test "OSC: 9;11: ConEmu comment incomplete -> desktop notification" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;11";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .show_desktop_notification);
    try testing.expectEqualStrings("11", cmd.show_desktop_notification.body);
}

test "OSC: 9;12: ConEmu mark prompt start 1" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;12";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .semantic_prompt);
}

test "OSC: 9;12: ConEmu mark prompt start 2" {
    const testing = std.testing;

    var p: Parser = .init(testing.allocator);
    defer p.deinit();

    const input = "9;12;abc";
    for (input) |ch| p.next(ch);

    const cmd = p.end('\x1b').?.*;
    try testing.expect(cmd == .semantic_prompt);
}
