from ursina import *
from ursina.prefabs.grid_editor import GridEditor
import re

class Tilemap(GridEditor):
    def __init__(self, tilemap='', tileset='', tileset_size=(8,8), **kwargs):
        if isinstance(tilemap, str):
            self.tilemap = load_texture(tilemap)

        self.grid = [[self.tilemap.get_pixel(x,y) for y in range(self.tilemap.height)] for x in range(self.tilemap.width)]
        super().__init__(
            texture=self.tilemap,
            size=self.tilemap.size,
            palette=(color.white, color.black, color.green, color.blue, color.red),
            edit_mode=False,
            **kwargs)

        self.tileset = tileset
        self.tileset_size = tileset_size
        self.model = Mesh()
        self.texture = tileset
        self.colliders = list()
        # self.texture.filtering = None

        # self.grid = [[self.tilemap.get_pixel(x,y) for y in range(self.h)] for x in range(self.w)]
        self.auto_render = False
        self.outline = Entity(parent=self, model=Quad(segments=0, mode='line', thickness=1), color=color.cyan, z=.01, origin=(-.5,-.5), enabled=self.edit_mode)

        self._quad = Quad(segments=0)
        self._quad.vertices = [Vec3(*v)+Vec3(.5,.5,0) for v in self._quad.vertices]
        self._garbage = Entity(parent=self, add_to_scene_entities=False)

        self.uv_dict = {
            '11111111' : [(4,1), (5,1), (6,1), (7,1)],     # fill

            '0.11111.' : [(1,3), (2,3), (5,3), (6,3)],     # top
            '1.0.1111' : [(3,2), (3,1)],     # right
            '111.0.11' : [(2,0), (1,0), (6,2), (5,2)],     # bot
            '11111.0.' : [(0,1), (0,2)],     # left

            '000.111.' : [(3,3), ],     # corner_top_right
            '1.000.11' : [(3,0), ],     # corner_bot_right
            '111.000.' : [(0,0), ],     # corner_bot_left
            '0.111.00' : [(0,3), ],     # corner_top_left

            '10111111' : [(1,1), ],     #inner_corner_bot_left
            '11101111' : [(1,2), ],     #inner_corner_top_left
            '11111011' : [(2,2), ],     #inner_corner_top_right
            '11111110' : [(2,1), ],     #inner_corner_bot_right

        }
        self.single_block_coordinates = [(4,0), (5,0), (6,0), (7,0)]
        self.variation_chance = [0,0,0,0,1,1,1,2,2,3]

        if 'min' in self.texture.name:
            self.uv_dict = {
                '11111111' : [(1,1)],     # fill

                '0.11111.' : [(1,2)],     # top
                '111.0.11' : [(1,0), ],     # bot
                '1.0.1111' : [(0,1), '-1,1'],     # right
                '11111.0.' : [(0,1)],     # left

                '0.111.00' : [(0,2), ],     # corner_top_left
                '000.111.' : [(0,2), '-1,1'],     # corner_top_right
                '1.000.11' : [(0,2), '-1,-1'],     # corner_bot_right
                '111.000.' : [(0,2), '1,-1'],     # corner_bot_left

                '11111110' : [(2,0), ],     #inner_corner_bot_right
                '10111111' : [(2,0), '-1,1'],     #inner_corner_bot_left
                '11111011' : [(2,1), ],     #inner_corner_top_right
                '11101111' : [(2,1), '-1,1'],     #inner_corner_top_left
            }
            self.single_block_coordinates = [(2,2)]
            self.variation_chance = [0,]

        self.uv_margin = .002

        self.render()


    def update(self):
        if not self.edit_mode:
            return

        super().update()
        if mouse.left:
            self.draw_temp(self.cursor.position)


    def draw_temp(self, position):
        e = Entity(
            parent=self._garbage,
            model='quad',
            scale=Vec3(1/self.tilemap.width, 1/self.tilemap.height, 1) * self.brush_size,
            position=self.cursor.position,
            z=-.1,
            texture=self.texture,

            texture_scale=Vec2(1/self.tileset_size[0], 1/self.tileset_size[1]),
            texture_offset=Vec2(.33, .33),
            origin=(-.5,-.5),
            ignore=True,
            )
        if self.selected_char == self.palette[0]:
            e.color = window.color
            e.texture = None




    def input(self, key):
        super().input(key)
        if key == 'left mouse up':
            for e in self._garbage.children:
                destroy(e)


    def render(self):
        self.scale = self.tilemap.size
        self.model.clear()

        for e in self.colliders:
            destroy(e)
        self.colliders.clear()

        tile_size = Vec2(1/self.tileset_size[0], 1/self.tileset_size[1])

        i = 0
        for y in range(self.tilemap.height):

            collider = None
            for x in range(self.tilemap.width):
                col = self.grid[x][y]

                if col == color.white and collider: # end collider
                    collider = None

                if col != color.white: #
                    self.model.vertices.extend([Vec3(x/self.tilemap.width, y/self.tilemap.height, 0) + (v*1/self.tilemap.width) for v in self._quad.vertices]) # add quad vertices, but offset.
                    self.model.triangles.append([i+j for j in range(4)])

                    neighbours = list()
                    # register neighbours clockwise starting from the top
                    for offset in [(0,1), (1,1), (1,0), (1,-1), (0,-1), (-1,-1), (-1,0), (-1,1)]:
                        try:
                            neighbours.append(self.grid[x+offset[0]][y+offset[1]] != color.white)
                        except IndexError:
                            neighbours.append(1)

                    neighbours = ''.join([str(int(e)) for e in neighbours])

                    # if collider is None and neighbours != '11111111':
                    #     collider = Entity(
                    #         parent=self,
                    #         model='cube',
                    #         scale_x=Vec3(0, 1/self.tilemap.height, 1),
                    #         origin_x=-.5,
                    #         position=(x-.5, y/self.tilemap.height),
                    #         collider='box',
                    #         color=color.red
                    #         )
                    #     self.colliders.append(collider)
                    # if collider and neighbours == '11111111': # end collider if it's a middle block
                    #     collider = None
                    # if collider:
                    #     collider.scale_x += 1/self.tilemap.width

                    random.seed(y+x)
                    variation_index = random.choice(self.variation_chance)
                    tile_scale = '1,1'

                    for key, value in self.uv_dict.items():
                        if re.match(key, neighbours):
                            if isinstance(value[-1], str):
                                tile_scale = value[-1]

                            variation_index = min(variation_index, len(value)-1-int(tile_scale=='1,1'))
                            _x, _y = value[variation_index]
                            break
                        else:
                            _x, _y = self.single_block_coordinates[variation_index]

                    uv = [
                        Vec2(tile_size[0] * _x,     tile_size[1] * _y)     + Vec2(self.uv_margin, self.uv_margin),
                        Vec2(tile_size[0] * (_x+1), tile_size[1] * _y)     + Vec2(-self.uv_margin, self.uv_margin),
                        Vec2(tile_size[0] * (_x+1), tile_size[1] * (_y+1)) + Vec2(-self.uv_margin, -self.uv_margin),
                        Vec2(tile_size[0] * _x,     tile_size[1] * (_y+1)) + Vec2(self.uv_margin, -self.uv_margin),
                    ]
                    if tile_scale == '1,1':
                        pass
                    elif tile_scale == '-1,1':
                        a, b, c, d = uv
                        uv = [b, a, d, c]
                    elif tile_scale == '1,-1':
                        a, b, c, d = uv
                        uv = [d, c, b, a]
                    elif tile_scale == '-1,-1':
                        a, b, c, d = uv
                        uv = [c, d, a, b]


                    self.model.uvs.extend(uv)
                    i += 4


        self.model.generate() # call to create the mesh


    def save(self):
        for y in range(self.tilemap.height):
            for x in range(self.tilemap.width):
                self.tilemap.set_pixel(x, y, self.grid[x][y])

        if self.tilemap.path:
            self.tilemap.save(self.tilemap.path)
            print('saving:', self.tilemap.path)



if __name__ == '__main__':
    app = Ursina()
    EditorCamera()
    tilemap = Tilemap('tilemap_test_level', tileset='test_tileset', tileset_size=(8,4), parent=scene)
    tilemap.canvas.texture = 'tilemap_test_level'
    # tilemap = Tilemap('brick', tileset='tileset_cave', tileset_size=(8,4), parent=scene)
    camera.orthographic = True
    camera.position = tilemap.tilemap.size / 2
    camera.fov = tilemap.tilemap.height

    Text('press tab to toggle edit mode', origin=(.5,0), position=(-.55,.4))

    app.run()
