#import "Basic";
#import "File";
#import "Math";
#import "Sort";
#import "String";
#import "Hash_Table";

solve_day22 :: (test: bool) {
    input := read_entire_file("inputs/day22.txt");
    lines := split(input, "\n");

    bricks: [..]Brick;
    supports: Table(s64, [..]s64);
    supported_by: Table(s64, [..]s64);
    for lines {
        brick := parse_brick(it);
        brick.label = bricks.count;
        array_add(*bricks, brick);
    }

    quick_sort(bricks, (b1, b2) => b1.min.z - b2.min.z);

    settle(bricks);
    find_supports(bricks, *supports, *supported_by);

    part1, part2 := solve2(bricks, supports, supported_by);

    print("Part 1: %\nPart 2: %\n", part1, part2);
    //print("Part 1: %\n", solve1(*bricks));
}

solve2 :: (bricks: [..]Brick, supports: Table(s64, [..]s64), supported_by: Table(s64, [..]s64)) -> part1: s64, part2: s64 {
    part2 := 0;
    part1 := 0;

    for firstDisintegrated: 0..bricks.count - 1 {
        dummy_void: void;
        moved: Table(s64, void);

        table_add(*moved, firstDisintegrated, dummy_void);

        moreMoved := true;

        while moreMoved {
            moreMoved = false;

            for mightMove: 0..bricks.count - 1 {
                if table_contains(*moved, mightMove) || bricks[mightMove].min.z == 1  continue;

                supp,found_supp := table_find(*supported_by, mightMove);

                still_supported := false;

                if found_supp {
                    for supp {
                        if !table_contains(*moved, it) {
                            still_supported = true;
                            break;
                        }
                    }
                }

                if !still_supported {
                    table_add(*moved, mightMove, dummy_void);
                    moreMoved = true;
                }
            }
        }

        table_remove(*moved, firstDisintegrated);

        part2 += moved.count;

        if !moved.count  part1 += 1;

    }

    return part1, part2;
}

find_supports :: (bricks: [..]Brick, supports: *Table(s64, [..]s64), supported_by: *Table(s64, [..]s64)) {
    for brick: 0..bricks.count - 1 {
        for other: 0..bricks.count - 1 {

            if brick == other  continue;

            if blocks(bricks[other], bricks[brick]) {
                supports_array := table_find_pointer(supports, other);
                supported_by_array := table_find_pointer(supported_by, brick);
                dummy: void;

                if !supports_array {
                    a: [..]s64;
                    supports_array = table_add(supports, other, a);
                }
                if !supported_by_array {
                    a: [..]s64;
                    supported_by_array = table_add(supported_by, brick, a); 
                }

                array_add(supports_array, brick);
                array_add(supported_by_array, other); 

            }
        }
    }
}

settle :: (bricks: [..]Brick) {
    moved: bool = true;

    while moved {
        moved = false;
        bubble_sort(bricks, (b1, b2) => b1.min.z - b2.min.z);

        for *moveCand: bricks {
            if moveCand.min.z == 1 {
                continue;
            }

            can_move := true;

            for blockCand: bricks {
                if moveCand.label == blockCand.label  continue;

                if blocks(blockCand, moveCand) {
                    can_move = false;
                    break;
                }
            }

            if can_move {
                moveCand.min.z -= 1;
                moveCand.max.z -= 1;
                moved = true;
            }
        }
    }
}

solve1 :: (bricks: *[..]Brick) -> s64 {
    result: s64 = 0;



    return result;
}

blocks :: (other: Brick, brick: Brick) -> bool {
    if brick.min.z != other.max.z + 1  return false;

    for x: other.min.x..other.max.x {
        for y: other.min.y..other.max.y {
            if x >= brick.min.x && x <= brick.max.x &&
               y >= brick.min.y && y <= brick.max.y {
                return true;
            }
        }
    }

    return false;
}

parse_brick :: (line: string) -> Brick {
    ends := split(line, "~");
    first_numbers := split(ends[0], ",");
    second_numbers := split(ends[1], ",");
    brick: Brick = ---; 

    x1,_ := string_to_int(first_numbers[0], 10, s64);
    y1,_ := string_to_int(first_numbers[1], 10, s64);
    z1,_ := string_to_int(first_numbers[2], 10, s64);
    x2,_ := string_to_int(second_numbers[0], 10, s64);
    y2,_ := string_to_int(second_numbers[1], 10, s64);
    z2,_ := string_to_int(second_numbers[2], 10, s64);

    return .{
        label = 0,
        min = .{
            x = min(x1, x2),
            y = min(y1, y2),
            z = min(z1, z2),
        },
        max = .{
            x = max(x1, x2),
            y = max(y1, y2),
            z = max(z1, z2),
        },
    };

    
}

overlap :: (b1: Brick, b2: Brick) -> bool {
    return max(b1.min.x, b2.min.x) <= min(b1.max.x, b2.max.x) &&
           max(b1.min.y, b2.min.y) <= min(b1.max.y, b2.max.y);
}

Brick :: struct {
    label: s64;
    min: Coord;
    max: Coord;
}

operator == :: (b1: Brick, b2: Brick) -> bool {
    return b1.min == b2.min && b1.max == b2.max;
}

operator == :: (c1: Coord, c2: Coord) -> bool {
    return c1.x == c2.x && c1.y == c2.y && c1.z == c2.z;
}

print_brick :: (brick: Brick) {
    print("%,%,%~%,%,%", brick.min.x, brick.min.y, brick.min.z, brick.max.x, brick.max.y, brick.max.z);
}

#scope_file
Coord :: struct {
    x: s64;
    y: s64;
    z: s64;
}