import display
import urandom
import utime
import buttons
import leds
import ubinascii

SCREEN_WIDTH = 160
SCREEN_HEIGHT = 80
MAX_PAST_POSITIONS = 100
PIXEL_COLOR = (255, 100, 250)
LED_COLOR = (255, 45, 200)

SUBDIV_HORIZONTAL = 20
SUBDIV_VERTICAL = 10
TIME_ALIVE_MAX = 510

class Game:
    def __init__(self):
        self.past_positions = dict()
        self.past_positions_list = list()
        self.field_width = (int)(SCREEN_WIDTH / SUBDIV_HORIZONTAL)
        self.field_height = (int)(SCREEN_HEIGHT / SUBDIV_VERTICAL)
        self.playing_field = []
        self.playing_field_buf = []
        for i in range(SUBDIV_HORIZONTAL):
            self.playing_field.append([False] * SUBDIV_VERTICAL)
            self.playing_field_buf.append([False] * SUBDIV_VERTICAL)
        self.reset_random()
        self.screen = display.open()
    def live(self):
        for i in range(SUBDIV_HORIZONTAL):
            for j in range(SUBDIV_VERTICAL):
                n_l = self.playing_field[(i - 1) % SUBDIV_HORIZONTAL][j]
                n_r = self.playing_field[(i + 1) % SUBDIV_HORIZONTAL][j]
                n_u = self.playing_field[i][(j - 1) % SUBDIV_VERTICAL]
                n_b = self.playing_field[i][(j + 1) % SUBDIV_VERTICAL]
                n_lu = self.playing_field[(i + 1) % SUBDIV_HORIZONTAL][(j + 1) % SUBDIV_VERTICAL] 
                n_ru = self.playing_field[(i + 1) % SUBDIV_HORIZONTAL][(j - 1) % SUBDIV_VERTICAL] 
                n_lb = self.playing_field[(i - 1) % SUBDIV_HORIZONTAL][(j + 1) % SUBDIV_VERTICAL] 
                n_rb = self.playing_field[(i - 1) % SUBDIV_HORIZONTAL][(j - 1) % SUBDIV_VERTICAL] 
                neighbors = [n_l, n_r, n_u, n_b, n_lu, n_ru, n_lb, n_rb]
                n_count = 0
                for n in neighbors:
                    if(n):
                        n_count += 1
                if(n_count == 3):
                    #print("LIVE")
                    self.playing_field_buf[i][j] = True
                if(n_count < 2):
                    self.playing_field_buf[i][j] = False
                    #print("DIE")
                if(n_count > 3):
                    #print("DIE")
                    self.playing_field_buf[i][j] = False
        # How to copy in micro-python?
        for i in range(SUBDIV_HORIZONTAL):
            for j in range(SUBDIV_VERTICAL):
                self.playing_field[i][j] = self.playing_field_buf[i][j]
        # Hashing this
        to_hash = ""
        for i in range(SUBDIV_HORIZONTAL):
            for j in range(SUBDIV_VERTICAL):
                if(self.playing_field[i][j]):
                    to_hash = to_hash + "1" 
                else:
                    to_hash = to_hash + "0"
        a = ubinascii.a2b_base64(to_hash)
        to_hash = a
        # print(to_hash)
        # print(type(to_hash))
        if(to_hash in self.past_positions):
            # Cycled
            print("++++ Cycle Detected ++++")
            print("Time alive: {}".format(self.time_alive))
            self.cycle_detected = True
        else:
            self.past_positions[to_hash] = True
            self.past_positions_list.append(to_hash)
        if(len(self.past_positions_list) > MAX_PAST_POSITIONS):
            del self.past_positions[self.past_positions_list[0]]
            del self.past_positions_list[0]
        if(self.cycle_detected == False):
            self.time_alive += 1
    def render(self):
        self.screen.clear()
        for i in range(SUBDIV_HORIZONTAL):
            for j in range(SUBDIV_VERTICAL):
                if(self.playing_field[i][j]):
                    color = PIXEL_COLOR 
                else:
                    color = (0, 0, 0)
                a = self.field_width
                b = self.field_height
                self.screen.rect(a * i, b * j, a * (i + 1), b * (j + 1), col=color)
        fullness = self.time_alive / TIME_ALIVE_MAX 
        if(fullness > 1.0):
            fullness = 1.0
        leds.set_rocket(0, (int)(31 * fullness))
        leds.set_rocket(1, (int)(31 * fullness))
        leds.set_rocket(2, (int)(31 * fullness))
        for i in range(((int)(11 * fullness))):
            if(self.cycle_detected):
                leds.set(i, (255, 0, 0))
            else:
                leds.set(i, LED_COLOR)
        self.screen.update()
    def reset_random(self):
        for i in range(SUBDIV_HORIZONTAL):
            for j in range(SUBDIV_VERTICAL):
                if(urandom.randint(1, 3) == 1):
                    self.playing_field[i][j] = True
                else:
                    self.playing_field[i][j] = False
        for i in range(11):
            leds.set(i, (0, 0, 0))
        leds.set_rocket(0, 0)
        leds.set_rocket(1, 0)
        leds.set_rocket(2, 0)
        self.time_alive = 0
        self.cycle_detected = False

# Main
# Reset LEDs
urandom.seed(1337)
game = Game()
while True:
    game.render()
    utime.sleep_ms(300)
    if(buttons.read(buttons.BOTTOM_LEFT) & buttons.BOTTOM_LEFT != 0):
        game.reset_random()
    game.live()