solve_day14 :: (test: bool) {
    contents := read_entire_file(ifx test then "inputs/day14_test.txt" else "inputs/day14.txt");
    lines    := split(contents, cast(u8) #char "\n");

    defer array_free(lines);

    grid_min_x: s64 = S64_MAX;
    grid_max_x: s64 = 0;

    grid_y: s64 = 0;

    wall_points: [..]Wall_Point;

    for line: lines {
        wall_point_splits := split(line, " -> ");

        for wall_point_splits {
            wall_point := parse_wall(it);
            wall_point.next = wall_points.count;
            array_add(*wall_points, wall_point);

            grid_min_x = min(grid_min_x, wall_point.x);
            grid_max_x = max(grid_max_x, wall_point.x);

            grid_y = max(grid_y, wall_point.y);
        }

        wall_points[wall_points.count - 1].next = -1;
    }

    // Expand the grid 
    grid_min_x -= 135;
    grid_max_x += 140;

    grid_x_offset := 500 - grid_min_x;

    grid_x := grid_max_x - grid_min_x;

    grid_y += 2;

    for *wall_points  it.x -= grid_min_x;

    grid: []u8 = NewArray(grid_x * grid_y, u8);

    paint_grid(grid, grid_x, grid_y, wall_points);

    part1 := 0;
    part2 := 0;

    fallen := 0;
    
    while outer:= true {
        sand_x := grid_x_offset;
        sand_y := 0;

        while true {
            if grid[grid_x_offset] == #char "o" {
                part2 = fallen;
                break outer;
            }

            if (1 + sand_y) * grid_x + sand_x > grid.count {
                print("Grid: %, %\n", grid_x, grid_y);
                print("Sand: %, %\n", sand_x, sand_y);
            }
            assert(sand_x != 0 && sand_x != grid_x - 1, "Going out of bounds. X is % and grid width is between 0 and %.", sand_x, grid_x);

            if grid[(1 + sand_y) * grid_x + sand_x] == #char "." {
                sand_y += 1;
            } else if grid[(1 + sand_y) * grid_x + sand_x - 1] == #char "." {
                sand_y += 1;
                sand_x -= 1;
            }  else if grid[(1 + sand_y) * grid_x + sand_x + 1] == #char "." {
                sand_y += 1;
                sand_x += 1;
            } else {
                grid[sand_y * grid_x + sand_x] = #char "o";
                break;
            }

            if sand_y == grid_y - 1 {
                if part1 == 0  part1 = fallen;

                grid[sand_y * grid_x + sand_x] = #char "o";
                break;
            }

        }


        fallen += 1;
    }

    print("Part 1: %\n", part1);
    print("Part 2: %\n", part2);
}

parse_wall :: (text: string) -> Wall_Point {
    comma_index := find_index_from_left(text, cast(u8) #char ",");

    first,  first\_success := string_to_int(slice(text, 0, comma_index), 10, s64);
    second, second_success := string_to_int(slice(text, comma_index + 1, text.count - comma_index - 1), 10, s64);

    return .{
        x = first,
        y = second,
        next = -1,
    };
}

paint_grid :: (grid: []u8, width: s64, height: s64, walls: [..]Wall_Point) {
    memset(grid.data, xx #char ".", grid.count);

    i := 0;

    while outer:= i < walls.count {
        prev := walls[i];

        while true {
            defer i += 1;
            if walls[i].x != prev.x {
                min_x := min(walls[i].x, prev.x);
                max_x := max(walls[i].x, prev.x);

                for min_x..max_x {
                    grid[prev.y * width + it] = #char "#";
                }
            } else {
                min_y := min(walls[i].y, prev.y);
                max_y := max(walls[i].y, prev.y);

                for min_y..max_y {
                    grid[it * width + prev.x] = #char "#";
                }
            }

            if walls[i].next == -1  break;

            prev = walls[i];
        }
    }
}

#scope_file

Wall_Point :: struct {
    x:    s64;
    y:    s64;
    next: s64;
}