import sys
import time
import math
import pygame
from . import window
from . import animation
from pygame.locals import *

from PPlay.tiledtmxloader import *


pygame.init()

def special_round(value):
    """
    For negative numbers it returns the value floored,
    for positive numbers it returns the value ceiled.
    """
    # same as:  math.copysign(math.ceil(abs(x)), x)
    # OR:
    # ## versus this, which could save many function calls
    # import math
    # ceil_or_floor = { True : math.ceil, False : math.floor, }
    # # usage
    # x = floor_or_ceil[val<0.0](val)

    if value < 0:
        return math.floor(value)
    return math.ceil(value)


class TileMap():

    def __init__(self,filename, window):
        self.x_pixels = 0
        self.y_pixels = 0
        self.cam_world_pos_x = 0
        self.cam_world_pos_y = 0
        self.screen_width = 0
        self.screen_height = 0
        self.window_height = window.height
        self.window_width = window.width
        self.world_map = {}
        self.resources = {}
        self.renderer = {}
        self.sprite_layers = {}
        self.load(filename)

    def load(self,filename):
        self.world_map = tmxreader.TileMapParser().parse_decode(filename)
        # let see how many pixels it will use
        self.x_pixels = self.world_map.pixel_width
        self.y_pixels = self.world_map.pixel_height
        self.screen_width = min(self.window_width, self.world_map.pixel_width)
        self.screen_height = min(self.window_height, self.world_map.pixel_height)

        self.resources = helperspygame.ResourceLoaderPygame()
        self.resources.load(self.world_map)
        self.renderer = helperspygame.RendererPygame()
        self.sprite_layers = helperspygame.get_layers_from_map(self.resources)
        #filter layers
        self.sprite_layers = [layer for layer in self.sprite_layers if not layer.is_object_group]

    def render(self,window):
        # render the map
        for sprite_layer in self.sprite_layers:
            if sprite_layer.is_object_group:
                # we dont draw the object group layers
                # you should filter them out if not needed
                continue
            else:
                self.renderer.render_layer(window.screen, sprite_layer)

        #pygame.display.flip()

    def renderLayer(self,sprite_layer,window):
        if not sprite_layer.is_object_group:
            # we dont draw the object group layers
            # you should filter them out if not needed
            self.renderer.render_layer(window.screen, sprite_layer)


    def setCameraPosition(self,cam_world_pos_x,cam_world_pos_y):
        self.cam_world_pos_x = cam_world_pos_x
        self.cam_world_pos_y = cam_world_pos_y
        self.renderer.set_camera_position_and_size(cam_world_pos_x, \
                             cam_world_pos_y,self.screen_width, \
                             self.screen_height,"topleft")

    def getNumLayers(self):
        return len(self.world_map.layers)

    def getNumTiles(self):
        return self.world_map.width, self.world_map.height

    def toggleLayerVisibility(self,id):
        if (id<len(self.world_map.layers)):
            self.sprite_layers[id].visible = not self.sprite_layers[id].visible



    def addSpriteToLayer(self, id , sprite):

        tileSprite = helperspygame.SpriteLayer.Sprite(sprite.image, sprite.rect)
        self.sprite_layers[id].add_sprite(tileSprite)

    def check_collision_tile(self,sprite,pos_x,pos_y,step_x,step_y,coll_layer):
        tile_x = int(pos_x // coll_layer.tilewidth)
        tile_y = int(pos_y // coll_layer.tileheight)

        if coll_layer.content2D[tile_y][tile_x] is None:
            return True
        return False

    def get_tile_on(self,sprite,pos_x,pos_y,step_x,step_y,coll_layer):
        tile_x = int(pos_x // coll_layer.tilewidth)
        tile_y = int(pos_y // coll_layer.tileheight)

        if not (coll_layer.content2D[tile_y][tile_x] is None):
            return coll_layer.content2D[tile_y][tile_x]
        else:
            return []


    def check_collision(self,sprite, pos_x, pos_y, step_x, step_y, coll_layer, layerId):
        """
        Checks collision of the hero against the world. Its not the best way to
        handle collision detection but for this demo it is good enough.

        :Returns: steps to add to heros current position.
        """

        sprite_pos_x = pos_x
        sprite_pos_y = pos_y
        sprite_width = sprite.width
        sprite_height = sprite.height

        # create hero rect
        sprite_rect = pygame.Rect(0, 0, sprite_width, sprite_height)
        sprite_rect.center = (sprite_pos_x,sprite_pos_y)

        # find the tile location of the hero
        tile_x = int((sprite_pos_x) // coll_layer.tilewidth)
        tile_y = int((sprite_pos_y) // coll_layer.tileheight)

        layer = self.world_map.layers[layerId]
        numTilesX = self.world_map.width
        numTilesY = self.world_map.height

        # find the tiles around the hero and extract their rects for collision
        tiles = []
        tilesId = []
        tile_rects = []
        for diry in (-1, 0 , 1):
            for dirx in (-1, 0, 1):
                if coll_layer.content2D[tile_y + diry][tile_x + dirx] is not None:
                    tilesId.append(layer.content2D[tile_y + diry][tile_x + dirx])
                    tiles.append(coll_layer.content2D[tile_y + diry][tile_x + dirx])
                    tile_rects.append(coll_layer.content2D[tile_y + diry][tile_x + dirx].rect)

        # save the original steps and return them if not canceled
        res_step_x = step_x
        res_step_y = step_y

        collided_tiles = []

        # x direction, floor or ceil depending on the sign of the step
        step_x = special_round(step_x)
        collidedIndexX = sprite_rect.move(step_x, 0).collidelist(tile_rects)
        # detect a collision and dont move in x direction if colliding
        if collidedIndexX > -1:
            res_step_x = 0
            collided_tiles.append(tilesId[collidedIndexX])

        # y direction, floor or ceil depending on the sign of the step
        step_y = special_round(step_y)

        collidedIndexY = sprite_rect.move(0,step_y).collidelist(tile_rects)
        # detect a collision and dont move in y direction if colliding
        if collidedIndexY > -1:
            res_step_y = 0
            collided_tiles.append(
                tilesId[collidedIndexY])


        # return the step the hero should do
        return res_step_x, res_step_y, collided_tiles

    #  -----------------------------------------------------------------------------
