""" day_09_02.py """

# usage: python3 day_09_02.py

from itertools import combinations
from itertools import pairwise


def get(filename):
    """ contents of filename """
    with open(filename, 'r', encoding='utf-8') as infile:
        data = infile.read()

    return data


def test(data, given_solution):
    """ testing """
    assert solve(data) == given_solution


def area(p1, p2):
    """ area of rectangle with opposite corners """
    (x1, y1), (x2, y2) = p1, p2
    return (abs(x1 - x2) + 1) * (abs(y1 - y2) + 1)


def vertical(p1, p2):
    """ vertical line """
    (x1, _), (x2, _) = p1, p2
    return x1 == x2


def outline(corners, colour):
    """ draw outline in colour """
    def draw_horizontal(pt1, pt2):
        """ horizontal line """
        (x1, y1), (x2, _) = sorted([pt1, pt2])
        for x in range(x1 + 1, x2):
            plot[(x, y1)] = colour

    def draw_vertical(pt1, pt2):
        """ vertical line """
        (x1, y1), (_, y2) = sorted([pt1, pt2], key=lambda i: i[1])
        for y in range(y1 + 1, y2):
            plot[(x1, y)] = colour

    ps = corners[:] + [corners[0]]
    plot = {}
    for p1, p2 in pairwise(ps):
        plot[p1] = '#'
        if vertical(p1, p2):
            draw_vertical(p1, p2)
        else:
            draw_horizontal(p1, p2)

    return plot


def outer_fill(grid, ns=None):
    """ fill outside outline """
    def neighbours(p1, grid):
        """ empty neighbours of p1 """
        deltas = [(0, -1), (1, 0), (0, 1), (-1, 0)]
        x, y = p1
        return [(x + dx, y + dy) for dx, dy in deltas
                if (grid.get((x + dx, y + dy), '.') == '.' and
                    0 <= x + dx < width and 0 <= y + dy < height)]

    width = max(x for x, y in grid.keys() if grid.get((x, y), '*') != '*') + 2
    height = max(y for x, y in grid.keys() if grid.get((x, y), '*') != '*') + 2

    if ns is None:
        nbs = [(0, 0)]
    else:
        nbs = ns

    if nbs:
        nxt = {(x, y): '*' for x, y in nbs}
        nyt = [i for j in [neighbours(p, grid.copy() | nxt)
                           for p in nbs] for i in j]
        nyt = list(set(nyt))
        return outer_fill(grid.copy() | nxt, ns=nyt)

    return grid.copy()


def fill(grid, colour):
    """ fill inside outline """
    g = outer_fill(grid)
    width = max(x for x, _ in g.keys()) + 1
    height = max(y for _, y in g.keys()) + 1
    for y in range(height):
        for x in range(width):
            i = g.get((x, y), '.')
            if i == '*':
                del g[(x, y)]
            elif i == '.':
                g[(x, y)] = colour

    return g


def contained(p1, p2, grid):
    """ is rectangle contained in filled outline """
    (x1, y1), (x2, y2) = p1, p2
    xp, xq = min(x1, x2), max(x1, x2)
    yp, yq = min(y1, y2), max(y1, y2)
    for y in range(yp, yq + 1):
        for x in range(xp, xq + 1):
            if grid.get((x, y), '.') == '.':
                return False

    return True


def pretty(grid):  # debug
    """ pretty print """
    width = max(x for x, _ in grid.keys()) + 2
    height = max(y for _, y in grid.keys()) + 2
    y = 0
    while y < height:
        x = 0
        while x < width:
            symbol = grid.get((x, y), '.')
            print(f' {symbol} ', end='')
            x += 1
        print()
        y += 1
    print()


def solve(data):
    """ solve the puzzle """
    red_tiles = [tuple(map(int, row.split(','))) for row in data.splitlines()]

    floor = outline(red_tiles, 'X')
    floor = fill(floor, 'X')

    return max(area(t1, t2) for t1, t2 in combinations(red_tiles, 2)
               if contained(t1, t2, floor))


if __name__ == '__main__':
    test(get('example01'), 24)

    puzzle = get('input')
    solution = solve(puzzle)

    print(solution)
