import sys
from matrix import load_matrix_file, get_neighbors, get_size
import shared
from scipy import ndimage
import imageio
from skimage.measure import euler_number, label
from skimage.morphology import flood_fill, flood
import matplotlib.pyplot as plt
import numpy as np
from math import prod
import random


class Smoke:
    def __init__(self, name):
        self.footprint = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]])
        self.parse(name)

    def parse(self, name):
        _split = lambda line: list(map(int, [l for l in line]))
        self.mx = np.array(load_matrix_file(name, func=_split))
        self.mx = np.pad(self.mx, pad_width=1, mode="constant", constant_values="9")
        self.set_values()
        self.set_coords()

    def set_values(self):
        self.lows = (
            ndimage.generic_filter(self.mx, np.min, footprint=self.footprint) == self.mx
        )
        self.low_values = self.mx[self.lows][self.mx[self.lows] < 9]
        self.highs = (
            ndimage.generic_filter(self.mx, np.max, footprint=self.footprint) == self.mx
        )
        mask = self.mx != 9
        self.mask = mask.astype(int)

    def set_coords(self):
        lc = []
        for idr, row in enumerate(self.lows):
            if idr == 0 or idr == len(self.lows) - 1:
                continue
            for idc, col in enumerate(row):
                if idc == 0 or idc == len(row) - 1:
                    continue
                if col:
                    lc.append((idr, idc))
        self.low_coords = lc

    def total_low_values(self):
        return (self.low_values + 1).sum()

    def find_chunks(self):
        cell_sizes = []
        self.cells = []
        by_center = []
        for x, y in self.low_coords:
            current = np.zeros_like(self.mx)
            m = flood_fill(self.mask, (x, y), -1, connectivity=1)
            current_cell = (m == -1).astype(int)
            self.cells.append(current_cell)
            cells = np.argwhere(current_cell == 1)
            by_center.append((len(cells), (x, y), cells))
            cell_sizes.append(len(cells))
        cells_sorted = list(set(sorted(cell_sizes)))
        self.by_center = by_center
        largest = cells_sorted[-3:]
        assert len(largest) == 3
        self.largest = largest
        return prod(largest)

    def animate(self, save=False):
        breakpoint()
        largest = [x for x in self.by_center if x[0] in self.largest][-3:]
        current = np.zeros_like(self.mx)
        shuf = self.by_center[:]
        random.shuffle(shuf)

        last_file = 0

        for idx, (length, xy, c) in enumerate(shuf):
            last_file = idx
            if length in largest:
                continue

            for x, y in c:
                current[y][x] = 1

            fig, ax = plt.subplots()
            ax.imshow(current, cmap=plt.cm.gray)
            ax.axis("off")
            if save:
                plt.savefig(f"09/{idx:03}.png")
            plt.close()

        lg = np.zeros_like(self.mx)
        for _, _, c in largest:
            for x, y in c:
                lg[y][x] = 1

        fig, ax = plt.subplots()
        ax.imshow(lg)
        ax.axis("off")
        plt.savefig(f"09/largest.png")
        plt.close()

        with imageio.get_writer("09/day09.gif", mode="I") as writer:
            names = [f"09/{x:03}.png" for x in range(334)]
            for filename in names:
                try:
                    image = imageio.imread(filename)
                    writer.append_data(image)
                except FileNotFoundError:
                    pass

            for x in range(15):
                image = imageio.imread("09/largest.png")
                for x in range(5):
                    writer.append_data(image)
                image = imageio.imread(f"09/{last_file:03}.png")
                for x in range(5):
                    writer.append_data(image)


def main():
    s = Smoke(shared.get_fname(9))
    print(s.total_low_values())
    print(s.find_chunks())
    if sys.argv[-1] != "--sample":
        s.animate(1)


if __name__ == "__main__":
    main()
