const std = @import("std");
const c = @cImport({
    @cInclude("openssl/aes.h");
    @cInclude("openssl/err.h");
});

const stdout = std.io.getStdOut().writer();

fn print(comptime str: []const u8, args: anytype) void {
    stdout.print(str, args) catch unreachable;
}

fn println(comptime str: []const u8, args: anytype) void {
    print(str ++ "\n", args);
}

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();

    var alloc = arena.allocator();

    const args = try std.process.argsAlloc(alloc);

    const base_text = "6CEA122F3B42975BDBBEB7F2C6EFAF9FD5A54FDD6233276C55358F4FBCB7A9492D0451B7019C69FAEF5FD23103FF7EC521FBBC6516CA2CB2CA663D5DBFF86BCF";
    var text = try alloc.alloc(u8, base_text.len);
    defer alloc.free(text);
    std.mem.copyForwards(u8, text, base_text);

    if (args.len == 2) {
        if (args[1].len != 128) {
            println("Invalid args", .{});
            return;
        }

        text = args[1];

        std.mem.copyForwards(u8, text, args[1]);
        println("Using cipher={s}", .{text});
    }

    var cipher = try alloc.alloc(u8, text.len / 2);
    var chs = try alloc.alloc(u8, text.len / 2);

    for (0..(text.len / 2)) |_i| {
        var i = _i * 2;
        var e = i + 2;
        var r = try std.fmt.parseInt(u8, text[i..e], 16);
        cipher[_i] = r;
        chs[_i] = r;
    }

    var plain = "Please meet me at Guildford Station: 7:00 be prompt!";
    var plair = "                                     8 3            ";

    var number_of_blocks = text.len / 2 / 16;

    println("blocks:", .{});

    for (0..number_of_blocks) |i| {
        var start = i * 16;
        var end = start + 16;
        if (end > plain.len) {
            end = plain.len;
        }
        println("{}: {s}", .{ i + 1, plain[start..end] });
        println("   {s}", .{plair[start..end]});
    }

    var changes = std.ArrayList(usize).init(alloc);
    defer changes.deinit();

    for (plair, 0..) |char, i| {
        if (char != ' ') {
            try changes.append(i);
        }
    }

    for (changes.items) |a| {
        println("Change on pos: {} '{c}'(0x{x:0>2}) -> '{c}'(0x{x:0>2})", .{ a, plain[a], plain[a], plair[a], plair[a] });
        println("This will update on block {} at pos {}", .{ 1 + (a - 16) / 16, 1 + (a - 16) % 16 });
        println("Current cypher 0x{x:0>2}", .{cipher[a - 16]});
        var after_aes = cipher[a - 16] ^ plain[a];
        println("Value after aes func curent ^ plain = 0x{x:0>2}", .{after_aes});
        var new_value = after_aes ^ plair[a];
        println("New value after_aes ^ to_replace = 0x{x:0>2}", .{new_value});
        println("Update cypher 0x{x:0>2} -> 0x{x:0>2}", .{ cipher[a - 16], new_value });
        chs[a - 16] = new_value;
        println("\n\n\n\n", .{});
    }

    print("Old cipher {}: ", .{cipher.len});
    for (cipher, chs) |item, is_change| {
        if (item != is_change) {
            print("{c}[1m", .{std.ascii.control_code.esc});
        }
        print("{x:0>2}", .{item});
        if (item != is_change) {
            print("{c}[0m", .{std.ascii.control_code.esc});
        }
    }
    println("", .{});
    print("               ", .{});
    for (cipher, chs) |item, is_change| {
        if (item != is_change) {
            print("\\/", .{});
        } else {
            print("  ", .{});
        }
    }
    println("", .{});
    print("New cipher {}: ", .{cipher.len});
    for (cipher, chs) |item, is_change| {
        if (item != is_change) {
            print("{c}[1m", .{std.ascii.control_code.esc});
        }
        print("{x:0>2}", .{is_change});
        if (item != is_change) {
            print("{c}[0m", .{std.ascii.control_code.esc});
        }
    }
    println("", .{});
}
