from coopgame.harfang.harfangGameTemplate import HarfangGameTemplate
from coopgame.harfang.harfangPrimitives import draw_circle, draw_triangle, draw_square
from coopgame.harfang.harfangColorConverter import harfang_color_from_coopcolor
from cooptools.colors import Color
from coopstructs.vectors import Vector2
from noise import snoise2, pnoise3
from coopbugger.monitoredclass import MonitoredClass


REZ = 15
MAX_SIZE = 20
PARTICLE_COLOR = Color.DARK_BLUE

class Particle():
    def __init__(self, pos: Vector2, value, size, color = None):
        self.pos = pos
        self.size = size
        self.thickness = 1
        self.value = value
        self.color = color

    def display(self, color: Color = None, **kwargs):
        if color is None:
            color = self.color
        # draw_triangle(kwargs.get('plus'),
        #               self.pos + Vector2(-self.size / 2, -self.size / 2),
        #               self.pos + Vector2(self.size / 2, -self.size / 2),
        #               self.pos + Vector2(0, self.size / 2),
        #               harfang_color_from_coopcolor(coopcolor=color))
        draw_square(kwargs.get('plus'),
                    self.pos,
                    self.size)

class HarfangPNoise(HarfangGameTemplate, MonitoredClass):
    def initialize_game(self):
        pass

    def handle_right_click(self, pressed_keys):
        pass

    def handle_hover_over(self, mouse_pos_as_vector: Vector2):
        pass

    def handle_mouse_scroll_up(self, pressed_keys):
        pass

    def handle_mouse_scroll_down(self, pressed_keys):
        pass

    def model_updater(self, delta_time_ms: int):
        pass

    def sprite_updater(self, delta_time_ms: int):
        pass

    def on_resize(self):
        pass

    def register_button(self, id, text, callback, postion_rect):
        pass

    def register_keys(self):
        pass

    def __init__(self):
        HarfangGameTemplate.__init__(self)
        MonitoredClass.__init__(self)
        self.cols = int(self.screen_width / REZ + 1)
        self.rows = int(self.screen_height /REZ + 1)

        self.xoff = 0
        self.yoff = 0
        self.zoff = 0
        self.xyincrement = .002
        self.zincrement = 0

        self.noise_scale = max(self.rows, self.cols)/5
        self.particles = []

        self._blitables = {}
        self.show_overlap = False


        self.create_particles()

        self.line_surface_layers = []
        self.lines_surface = None
        self.lines_surface_offset = Vector2(0, 0)

    @MonitoredClass.timer
    def draw(self, frames:int, debug_mode: bool = False):
        self.xoff += self.xyincrement
        self.yoff += self.xyincrement
        self.zoff += self.zincrement

        self.update_particles()

        for row in self.particles:
            for particle, color in row:
                particle.display(plus=self.plus, color=color)

        self._draw_fps(self.runtime_config)

    def handle_left_click(self, pressed_keys):
        self.show_overlap = not self.show_overlap

    @MonitoredClass.timer
    def update_particles(self):

        if self.particles is None or len(self.particles) == 0:
            self.create_particles()

        octaves = 6
        persistence = .5
        lacunarity = 2


        for ii in range(0, self.cols):
            # self.particles.append([])
            for jj in range(0, self.rows):
                # color = (rnd.randint(0, 255), rnd.randint(0, 255), rnd.randint(0, 255))
                value = pnoise3(
                    (ii) / self.noise_scale + self.xoff,
                    (jj) / self.noise_scale + self.yoff,
                    self.zoff,
                    octaves=octaves,
                    persistence=persistence,
                    lacunarity=lacunarity,
                    repeatx=1024,
                    repeaty=1024,
                    base=0)
                # color = Color.closest_color((0, 0, int(255 * (value + 1 / 2))))
                color = (0, 0, int(255 * (value + 1 / 2)))

                self.particles[ii][jj][0].value = value
                self.particles[ii][jj][0].size = max(int((value + 1 / 2) * MAX_SIZE), 1)
                self.particles[ii][jj][0].color = color



    @MonitoredClass.timer
    def create_particles(self):
        self.particles.clear()

        for ii in range(0, self.cols):
            self.particles.append([])
            for jj in range(0, self.rows):
                self.particles[ii].append(
                    (Particle(Vector2(ii * REZ, jj * REZ), 0, 1), PARTICLE_COLOR))

    # @MonitoredClass.timer
    # def draw_lines_to_surface(self, surface: pygame.Surface, only_last=False):
    #     counter = 0
    #     for ii in range(0, self.cols-1):
    #         for jj in range(0, self.rows-1):
    #             if (only_last and not((ii == self.cols - 3) or (jj == self.rows - 3))):
    #                 continue
    #
    #             vals = self.particles[ii][jj][0].value, self.particles[ii+1][jj][0].value, self.particles[ii+1][jj+1][0].value, self.particles[ii][jj+1][0].value
    #             state = self.get_state_from_square(*[math.ceil(x) for x in vals])
    #             lines = self.line_state_switch(state)
    #             crit_points = self.crit_points_from_square(self.particles[ii][jj][0].pos, REZ, *vals)
    #             # crit_points = self.square_crits_of_point(self.particles[ii][jj][0].pos, REZ)
    #             for line in lines:
    #                 pygame.draw.line(surface, Color.WHITE.value, (crit_points[line[0]].x, crit_points[line[0]].y), (crit_points[line[1]].x, crit_points[line[1]].y))
    #                 counter += 1
    #
    #     logging.debug(f"{counter} lines drawn")
    #
    # @MonitoredClass.timer
    # def draw_lines(self):
    #     if self.line_surface_layers is not None and len(self.line_surface_layers) > 0:
    #         print(len(self.line_surface_layers))
    #         for surface, offset in self.line_surface_layers:
    #             self.screen.blit(surface, (offset.x, offset.y))
    #     else:
    #         self.draw_lines_to_surface(self.screen)


    @MonitoredClass.timer
    def get_state_from_square(self, a, b, c, d):
        # return 1*a + 2*b +4*c+8*d
        return 8 * a + 4 * b + 2 * c + 1 * d


    @MonitoredClass.timer
    def line_state_switch(self, state):
        switcher = {
            0:[],
            1:[(2, 3)],
            2:[(1, 2)],
            3:[(1, 3)],
            4:[(0, 1)],
            5:[(0, 3), (1, 2)],
            6:[(0, 2)],
            7:[(0, 3)],
            8:[(0, 3)],
            9:[(0, 2)],
            10:[(0, 1), (2, 3)],
            11:[(0, 1)],
            12:[(1, 3)],
            13:[(1, 2)],
            14:[(2, 3)],
            15:[],
        }
        return switcher.get(state, [])

    @MonitoredClass.timer
    def crit_points_from_square(self, point: Vector2, resolution, val1, val2, val3, val4):
        """:returns a tuple of the critical points based on the position of the top left of the square and the
        values of the 4 corners of the square. ordered TL, TR, BR, BL (clockwise starting from top left)
        values assumed to be between -.5 and .5"""

        val1 +=1
        val2 +=1
        val3 +=1
        val4 +=1


        amtA = (1 - val1)/(val2 - val1) if val1 != val2 else .5
        a = Vector2(point.x + resolution * amtA, point.y)
        # a = point.interpolate(point+Vector2(resolution, 0), amtA)
        amtB = (1 - val2)/(val3 - val2) if val3 != val2 else .5
        b = Vector2(point.x + resolution, point.y + resolution * amtB)
        # b = (point+Vector2(resolution, 0)).interpolate(point + Vector2(resolution, resolution), amtB)
        amtC = (1 - val3) / (val4 - val3) if val3 != val4 else .5
        c = Vector2(point.x + resolution * (1 - amtC),  point.y + resolution)
        # c = (point + Vector2(resolution, resolution)).interpolate(point + Vector2(0, resolution), amtC)
        amtD = (1 - val4) / (val1 - val4) if val1 != val4 else .5
        d = Vector2(point.x, point.y + resolution * (1 - amtD))
        # d = (point + Vector2(0, resolution)).interpolate(point, amtD)

        return (a, b, c, d)


    @MonitoredClass.timer
    def square_crits_of_point(self, point:Vector2, resolution):
        a = Vector2(point.x + resolution/2  , point.y)
        b = Vector2(point.x + resolution    , point.y + resolution/2)
        c = Vector2(point.x + resolution/2  , point.y + resolution)
        d = Vector2(point.x                 , point.y + resolution/2)
        return (a, b, c, d)


if __name__ == "__main__":
    import logging
    logging.basicConfig(level=logging.INFO)
    game = HarfangPNoise()
    game.main()