//! Markdown Parser.
//!
//! Leaf blocks
//!     Thematic Breaks
//!     ATX headings
//!     Setext headings
//!     Indented code blocks
//!     Fenced code blocks
//!     HTML blocks
//!     Link reference definitions
//!     Paragraphs
//!     Blank lines
//!
//! Container blocks
//!     Block Quotes
//!     List Items
//!     Lists
//!
//! Inlines
//!     Code spans
//!     emph, strong
//!     Links
//!     Images
//!     Autolinks
//!     Raw HTML
//!     Hard line breaks
//!     Soft line breaks
//!     Text

const BlockType = enum {
    // Leaf Blocks
    Break,
    Heading,
    Code,
    HTML,
    LinkDefinition,
    Paragraph,
    Blank,

    // Container Blocks
    Quote,
    List,
    // ListItem,
};

const Block = struct {
    type: BlockType,
    text: []const u8,
    depth: u8 = 0,
};

const std = @import("std");
const md = @import("md");

pub fn render(src: []const u8, alloc: std.mem.Allocator) !void {
    var it = std.mem.splitScalar(u8, src, '\n');

    var blocks = std.ArrayList(Block).init(alloc);
    defer blocks.deinit();

    line_search: while (it.next()) |line| {
        // early check for blank line
        for (line) |ch| {
            if (!std.ascii.isWhitespace(ch)) break;
        } else {
            try blocks.append(.{ .type = .Blank, .text = line });
            continue :line_search;
        }

        // figure out indentation
        var depth: u8 = 0;
        var start: u8 = 0;

        for (line) |ch| {
            switch (ch) {
                ' ' => depth += 1,
                '\t' => depth += 4,
                else => break,
            }
            start += 1;
        }
        // at this point, start will not be line.len because we checked
        // it is not blank

        if (depth >= 4) {
            // todo something special has to happen. lists and code blocks.
        }

        // test thematic break
        switch (line[start]) {
            '*', '-', '_' => |kind| {
                var count: u8 = 0;
                for (line[start..]) |ch| {
                    if (ch == kind) {
                        count += 1;
                        continue;
                    }

                    if (std.ascii.isWhitespace(ch)) continue;

                    break;
                } else {
                    if (count >= 3) {
                        try blocks.append(.{ .type = .Break, .text = line });
                        continue :line_search;
                    }
                }
            },
            else => {},
        }
    }

    for (blocks.items) |block| {
        std.debug.print("{any}\n", .{block});
    }

    // TODO split into paragraphs, list elements
    //

    // const State = enum {
    //     Block,
    // };

    // mach: switch (State.Block) {
    //     .Block => {
    //         continue :mach .Block;
    //     },
    // }

    // while (it.next()) |line| {}
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const alloc = gpa.allocator();

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

    const buf = try alloc.alloc(u8, 1 << 20);
    defer alloc.free(buf);

    for (args[1..]) |path| {
        const file = try std.fs.cwd().openFile(path, .{ .mode = .read_only });
        defer file.close();
        const stat = try file.stat();
        if (stat.size > buf.len)
            return error.FileTooBig;

        const n = try file.readAll(buf);
        const content = buf[0..n];

        try render(content, alloc);
    }
}
