import shared
import itertools
import json
import math
from functools import reduce

red = lambda word: f"\u001b[31m{word}\u001b[0m"
yellow = lambda word: f"\u001b[33m{word}\u001b[0m"
blue = lambda word: f"\u001b[34m{word}\u001b[0m"
green = lambda word: f"\u001b[32m{word}\u001b[0m"

class Snailfish:
    def __init__(self):
        self.load()

    def load(self):
        snailfish = []
        with open(shared.get_fname(18), "r") as f:
            for line in f.readlines():
                snailfish.append(json.loads(line.rstrip()))
                print(snailfish[-1])
        self.snailfish = snailfish

    def run(self):
        reduced = reduce(self.add, self.snailfish)
        print(green(reduced))
        self.magnitude_sum = self.magnitude(reduced)
        permutations = itertools.permutations(self.snailfish, 2)
        self.magnitude_max = 0
        for a,b in permutations:
            print(red(a), yellow(b))
            fish_sum = self.add(a,b)
            print("\t", blue(fish_sum))
            mag = self.magnitude(fish_sum)
            if mag > self.magnitude_max:
                self.magnitude_max = mag
            print()
        
    def add_left(self, fish, value):
        if value is None:
            return fish
        if isinstance(fish, int):
            return fish + value
        return [self.add_left(fish[0], value), fish[1]]

    def add_right(self, fish, value):
        if value is None:
            return fish
        if isinstance(fish, int):
            return fish + value
        return [fish[0], self.add_right(fish[1], value)]

    def explode(self, fish, depth=4):
        if isinstance(fish, int):
            return False, None, fish, None
        if depth == 0:
            return True, fish[0], 0, fish[1]
        x, y = fish
        exploded, left, x, right = self.explode(x, depth - 1)
        if exploded:
            return True, left, [x, self.add_left(y, right)], None
        exploded, left, y, right = self.explode(y, depth - 1)
        if exploded:
            return True, None, [self.add_right(x, left), y], right
        return False, None, fish, None

    def split(self, fish):
        if isinstance(fish, int):
            if fish >= 10:
                return True, [fish // 2, math.ceil(fish / 2)]
            return False, fish
        x, y = fish
        split, x = self.split(x)
        if split:
            return True, [x, y]
        split, y = self.split(y)
        return split, [x, y]

    def add(self, x, y):
        fish = [x, y]
        while True:
            exploded, _, fish, _ = self.explode(fish)
            if exploded:
                continue
            split, fish = self.split(fish)
            if not split:
                break
        return fish

    def magnitude(self, fish):
        if isinstance(fish, int):
            return fish
        return 3 * self.magnitude(fish[0]) + 2 * self.magnitude(fish[1])


def main():
    s = Snailfish()
    s.run()
    print('part1: sum', s.magnitude_sum)
    print('part2: max', s.magnitude_max)


if __name__ == "__main__":
    main()
