import matrix
import shared
import itertools
import functools
from pprint import pprint
from collections import defaultdict

SYMBOLS = "*%@#+-/$=&"


def get_all_numbers_and_starting_coords(mat):
    """
    for a coordinate, walks right until it encounters a non digit,
    when it does, it adds that number string to a collection,
    and starts over, walking right until it continues to next row
    """
    nums = []
    for y, row in enumerate(mat):
        x = 0
        while x < len(row):
            num = matrix.number_starting_at(mat, x, y)
            if num is None:
                x += 1
                continue
            nums.append(((x, y), num))
            x += len(num)
    return nums


def get_row_coords(x1, y, length):
    """
    for a given x,y point, it just creates the rest of the x/y coords to the right of this coord for a run
    """
    coords = []
    for x in range(x1, x1 + length):
        coords.append((x, y))
    return coords


def process_number(mx, coords, num):
    line_coords = get_row_coords(*coords, len(num))
    for coord in line_coords:
        any_symbols = [
            (cs, v) for cs, v in matrix.get_neighbor_coords(mx, *coord) if v in SYMBOLS
        ]
        if any_symbols:
            return True, any_symbols[0]
    return False, None


# @shared.profile
def part1(mat):
    choose = []
    nums = get_all_numbers_and_starting_coords(mat)
    for coords, num in nums:
        valid, symbol_coord = process_number(mat, coords, num)
        if valid:
            choose.append(num)
    total = 0
    for n in choose:
        total += int(n)
    print(total)


# @shared.profile
def part2(mat):
    splats = matrix.find_in_matrix(mat, "*", False)
    _nums = get_all_numbers_and_starting_coords(mat)

    symbols = defaultdict(list)
    nums = get_all_numbers_and_starting_coords(mat)
    for coords, num in nums:
        valid, sc = process_number(mat, coords, num)
        if valid:
            symbols[(sc[0]["r"], sc[0]["c"])].append(num)

    total = 0
    for splat in splats:
        neighbors = symbols[splat]
        if len(neighbors) != 2:
            continue
        ratio = int(neighbors[0]) * int(neighbors[1])
        total += ratio
    print(total)


def main():
    mat = shared.load_file_char_matrix(shared.get_fname(3))
    with shared.elapsed_timer() as elapsed:
        part1(mat)
        print("🕒", elapsed())

    with shared.elapsed_timer() as elapsed:
        part2(mat)
        print("🕒", elapsed())


if __name__ == "__main__":
    main()
