import pygame
import sprite_types
import inspect
import base
from utils import remove_from_list


class Img(object):

    map_class = "Img"

    def __init__(self, center=(None, None), file_name_or_obj=None, namehint="", collision_shape=None, container_obj=None):

        if not (len(center) == 2):
            raise TypeError("Center must be (x,y)")
        x = center[0]
        y = center[1]
        if not container_obj:
            if (x is None) or (y is None):
                raise TypeError("Center=(x,y) must be provided")
            else:
                self.container_obj = None
                self.center = (x, y)
        else:
            self.container_obj = container_obj  # Use weakrefs

        if (file_name_or_obj is None):
            raise TypeError(
                "file_name_or_obj must be filename, file_obj or surface")

        if not isinstance(file_name_or_obj, pygame.Surface):
            if namehint == "":
                self.surf = pygame.image.load(file_name_or_obj)
            else:
                self.surf = pygame.image.load(file_name_or_obj, namehint)
        else:
            # From surface
            # Todo: Some param for caching
            self._surf = file_name_or_obj

        if collision_shape:
            self._col_shape = collision_shape
        else:
            self._col_shape = self.bbox

    @property
    def col_shape(self):
        return self._col_shape

    @col_shape.setter
    def col_shape(self, value):
        self._col_shape = value

    def sync_col_shape(self):
        self.col_shape.center = self.center

    @property
    def center(self):
        if self.container_obj:
            return (self.container_obj.x, self.container_obj.y)
        else:
            return self._center

    @center.setter
    def center(self, val):
        if self.container_obj:
            self.container_obj.x, self.container_obj.y = val
        else:
            self._center = val
        self.sync_col_shape()

    @classmethod
    def from_container_obj(cls, container_obj, file_name_or_obj, namehint=""):
        return cls(file_name_or_obj=file_name_or_obj, namehint=namehint, container_obj=container_obj)

    @classmethod
    def from_surface(cls, center, surf):
        return cls(center=center, file_name_or_obj=surf)

    @property
    def surf(self):
        return self._surf

    @surf.setter
    def surf(self, value):
        if isinstance(value, pygame.Surface):
            self._surf = value
        else:
            return TypeError("surf can only be set to anther pygame.Surface")

    @property
    def center_x(self):
        return self.center[0]

    @property
    def center_y(self):
        return self.center[1]

    @center_x.setter
    def center_x(self, new_x):
        self.center[0] = new_x

    @center_y.setter
    def center_y(self, new_y):
        self.center[1] = new_y

    @property
    def x(self):
        return self.center_x

    @x.setter
    def x(self, val):
        self.center_x = val

    @property
    def y(self):
        return self.center_y

    @y.setter
    def y(self, val):
        self.center_y = val

    @property
    def bbox(self):
        '''Returns sprite_types.Rect copy of bounding box'''
        surf_rect = self.get_rect()
        return sprite_types.Rect.from_rect(surf_rect)

    def get_rect(self):
        '''Returns pygame.Rect copy of bounding box'''
        return self.surf.get_rect(center=self.center)

    def get_surface(self):
        ''' Returns a copy of internal surface '''
        return self.surf.copy()

    def __repr__(self):
        return "{0.__class__}( center={0.center}, size={0.size})".format(self)

    def __copy__(self):
        return self.__cls__.from_surface(self.center, self.surf.copy())

    def save_to_disk(self, filename):
        pygame.image.save(self.get_surface(), filename)
        return True

    @property
    def size(self):
        return self.surf.get_size()

    @size.setter
    def size(self, value):
        raise NotImplementedError(
            "Setting size is ambiguous, so not implemented")

    @property
    def width(self):
        return self.get_width()

    @width.setter
    def width(self, value):
        raise NotImplementedError(
            "Setting width is ambiguous, so not implemented")

    @property
    def height(self):
        return self.get_height()

    @height.setter
    def height(self, value):
        raise NotImplementedError(
            "Setting height is ambiguous, so not implemented")

    @property
    def w(self):
        return self.width

    @w.setter
    def w(self, value):
        self.width = value

    @property
    def h(self):
        return self.height

    @h.setter
    def h(self, value):
        self.height = value

    # Surface methods not provided, apply on self.surf
    def blit(self, surface, (x, y)=(None, None), clip_rect=None):

        if (x, y) == (None, None):
            (x, y) = self.center
        # pygame.Rect does this, which is used in clear_rect
        x, y = int(x), int(y)

        if clip_rect:
            # Set clip region
            surface.set_clip(clip_rect)

        surface.blit(self.surf, self.bbox.topleft)

        if clip_rect:
            # Remove clip region
            surface.set_clip(None)

    def draw(self):
        tmp_dirty_sprite = {
            "sprite": self,
            # later shape may be used for matching insted of sprites, as sprite
            # may be subclassed, changing "sprite" obj
            "shape": "Img",
            "bbox": self.get_rect(),
            "config": {
                "center": self.center,
                "file_name_or_obj": self.surf
            }
        }
        for tmp_old_sprite in self.container_obj.list_rendered_sprite:
            # if any of them is eqv. to new dirty sprite, mark old one as
            # still_valid
            if tmp_old_sprite == tmp_dirty_sprite:
                # puts "still_valid" entry in the old_dirty_sprite_dict
                # quite good approach, wont match with anything again
                # but "still_valid" should be removed, before next step (tick), to allow
                # checks later
                tmp_old_sprite["still_valid"] = True
                break
        else:
            # i.e. if no match found, list it as dirty sprite
            self.container_obj.list_dirty_sprite.append(tmp_dirty_sprite)

    # collide func
    def collide(self, other_obj, moved_center=(None, None), from_container_obj=False):
        ''' Collision handler for any type of obj'''

        if not hasattr(other_obj, "__class__"):
            raise AttributeError(
                "other_obj must be a class or instance")

        # Handle instances
        if isinstance(other_obj, sprite_types.Circle):
            return self.collide_circle(other_obj, moved_center, from_container_obj)
        elif isinstance(other_obj, sprite_types.Rect):
            return self.collide_rect(other_obj, moved_center, from_container_obj)
        elif isinstance(other_obj, Img):
            # Todo: add img collision
            return self.collide_img(other_obj, moved_center, from_container_obj)
        else:
            pass

        # Handle classes
        if inspect.isclass(other_obj):
            if issubclass(other_obj,  sprite_types.Circle):
                return self.collide_list_circle(other_obj.list_instance, moved_center, from_container_obj)
            elif issubclass(other_obj, sprite_types.Rect):
                return self.collide_list_rect(other_obj.list_instance, moved_center, from_container_obj)
            elif issubclass(other_obj, Img):
                # Todo: add img collision
                return self.collide_list_img(other_obj.list_instance, moved_center, from_container_obj)
            else:
                pass

        # Handle fun_Game and fun_Class or their instances
        if isinstance(other_obj, base.fun_Game) or (inspect.isclass(other_obj) and issubclass(other_obj, base.fun_Game)):
            def instance_collide(other_obj):
                return self.collide(other_obj.sprite, moved_center, from_container_obj=True)
            to_return = other_obj.for_all_instance(
                instance_collide, flatten="level-class")
            remove_from_list(to_return, self.container_obj)
            remove_from_list(to_return, None, recursive=True)
            remove_from_list(to_return, False, recursive=True)
            return to_return
        if inspect.isclass(other_obj) and issubclass(other_obj, base.fun_Class):
            def instance_collide(other_obj):
                return self.collide(other_obj.sprite, moved_center, from_container_obj=True)
            to_return = other_obj.for_all_instance(
                instance_collide, flatten=False)
            remove_from_list(to_return, self.container_obj)
            remove_from_list(to_return, None, recursive=True)
            remove_from_list(to_return, False, recursive=True)
            return to_return
        if isinstance(other_obj, base.fun_Class):
            other_obj_sprite = other_obj.sprite
            to_return = self.collide(
                other_obj_sprite, moved_center, from_container_obj=True)
            # doesnt return list, so prev. case operations not done
            return to_return

    def collide_img(self, another_img, moved_center=(None, None), from_container_obj=False):
        ''' Checks if this img intersects with another_img. '''

        does_collide = self.collide(
            another_img.col_shape, moved_center, from_container_obj=False)
        if does_collide:
            if from_container_obj:
                to_return = another_img.container_obj
            else:
                to_return = another_img
        else:
            to_return = False
        return to_return

    def collide_list_img(self, list_img, moved_center=(None, None), from_container_obj=False):
        list_collided_img = []
        for each_img in list_img:
            if self.collide_img(each_img, moved_center, from_container_obj):
                # Collide_img itself takes container_obj into account, so no
                # need of post-processing
                list_collided_img.append(each_img)
        else:  # else of the for loop
            return False
        return list_collided_img

    def collide_circle(self, another_circle, moved_center=(None, None), from_container_obj=False):
        ''' Checks if this circle intersects with another_circle. '''
        if not (len(moved_center) == 2):
            raise AttributeError("moved_center must be of the form (x,y)")
        _center = self.center
        moved_center = list(moved_center)
        if moved_center[0] is None:
            moved_center[0] = _center[0]
        if moved_center[1] is None:
            moved_center[1] = _center[1]
        # temporarily move self to moved_center and shift back later
        self.center = moved_center
        does_collide = another_circle.collide_img(self)
        self.center = _center
        if does_collide:
            if from_container_obj:
                to_return = another_circle.container_obj
            else:
                to_return = another_circle
        else:
            to_return = False
        return to_return

    def collide_point(self, (point_x, point_y)):
        ''' Checks if (point_x, point_y) is inside this circle. '''
        does_collide = self.col_shape.collide_point((point_x, point_y))
        if does_collide:
            return True
        else:
            return False

    def collide_list_circle(self, list_circle, moved_center=(None, None), from_container_obj=False):
        ''' Returns list of circles in list_circle which intersects with this circle. '''
        list_collided_circle = []
        for circle in list_circle:
            if self.collide_circle(circle, moved_center, from_container_obj):
                # Collide_circle itself takes container_obj into account, so no
                # need of post-processing
                list_collided_circle.append(circle)
        else:  # else of the for loop
            return False
        return list_collided_circle

    def collide_list_any_circle(self, list_circle, moved_center=(None, None), from_container_obj=False):
        ''' Checks if this circle intersects with any of the circles in circle_list. '''
        for circle in list_circle:
            if self.collide_circle(circle, moved_center, from_container_obj):
                return True
        else:  # else of the for loop
            return False

    def collide_list_all_circle(self, list_circle, moved_center=(None, None), from_container_obj=False):
        ''' Checks if this circle intersects with all of the circles in circle_list. '''
        for circle in list_circle:
            if not self.collide_circle(circle, moved_center, from_container_obj):
                return False
        else:  # else of the for loop
            return True

    def collide_rect(self, another_rect, moved_center=(None, None), from_container_obj=False):
        ''' Checks if this circle collides with another_rect. '''
        if not (len(moved_center) == 2):
            print "moved_center must be of the form (x,y)"
        _center = self.center
        moved_center = list(moved_center)
        if moved_center[0] is None:
            moved_center[0] = _center[0]
        if moved_center[1] is None:
            moved_center[1] = _center[1]

        self.center = moved_center
        does_collide = another_rect.collide_img(self)
        self.center = _center
        if does_collide:
            if from_container_obj:
                to_return = another_rect.container_obj
            else:
                to_return = another_rect
        else:
            to_return = False
        return to_return

    def collide_list_rect(self, list_rect, moved_center=(None, None), from_container_obj=False):
        ''' Returns list of rect in list_rect which intersects with this circle. '''
        list_collided_rect = []
        for rect in list_rect:
            if self.collide_rect(rect, moved_center, from_container_obj):
                list_collided_rect.append(rect)
        else:  # else of the for loop
            return False
        return list_collided_rect

    def collide_list_any_rect(self, list_rect, moved_center=(None, None), from_container_obj=False):
        ''' Returns True if collides with any of the rect in the list. '''
        for temp_rect in list_rect:
            if self.collide_rect(self, temp_rect, moved_center, from_container_obj):
                return True
        else:
            return False

    def collide_list_all_rect(self, list_rect, moved_center=(None, None), from_container_obj=False):
        ''' Returns True if collides with all the rect in the list. '''
        for temp_rect in list_rect:
            if not self.collide_rect(self, temp_rect, moved_center, from_container_obj):
                return False
            else:
                return True

dab2361a5a77dfaadbfc869fb573a78b09792539
import pygame
import sprite_types
import inspect
import base
from utils import remove_from_list


class Img(object):

    map_class = "Img"

    def __init__(self, center=(None, None), file_name_or_obj=None, namehint="", collision_shape=None, container_obj=None):

        if not (len(center) == 2):
            raise TypeError("Center must be (x,y)")
        x = center[0]
        y = center[1]
        if not container_obj:
            if (x is None) or (y is None):
                raise TypeError("Center=(x,y) must be provided")
            else:
                self.container_obj = None
                self.center = (x, y)
        else:
            self.container_obj = container_obj  # Use weakrefs

        if (file_name_or_obj is None):
            raise TypeError(
                "file_name_or_obj must be filename, file_obj or surface")

        if not isinstance(file_name_or_obj, pygame.Surface):
            if namehint == "":
                self.surf = pygame.image.load(file_name_or_obj)
            else:
                self.surf = pygame.image.load(file_name_or_obj, namehint)
        else:
            # From surface
            # Todo: Some param for caching
            self._surf = file_name_or_obj

        if collision_shape:
            self._col_shape = collision_shape
        else:
            self._col_shape = self.bbox

    @property
    def col_shape(self):
        return self._col_shape

    @col_shape.setter
    def col_shape(self, value):
        self._col_shape = value

    def sync_col_shape(self):
        self.col_shape.center = self.center

    @property
    def center(self):
        if self.container_obj:
            return (self.container_obj.x, self.container_obj.y)
        else:
            return self._center

    @center.setter
    def center(self, val):
        if self.container_obj:
            self.container_obj.x, self.container_obj.y = val
        else:
            self._center = val
        self.sync_col_shape()

    @classmethod
    def from_container_obj(cls, container_obj, file_name_or_obj, namehint=""):
        return cls(file_name_or_obj=file_name_or_obj, namehint=namehint, container_obj=container_obj)

    @classmethod
    def from_surface(cls, center, surf):
        return cls(center=center, file_name_or_obj=surf)

    @property
    def surf(self):
        return self._surf

    @surf.setter
    def surf(self, value):
        if isinstance(value, pygame.Surface):
            self._surf = value
        else:
            return TypeError("surf can only be set to anther pygame.Surface")

    @property
    def center_x(self):
        return self.center[0]

    @property
    def center_y(self):
        return self.center[1]

    @center_x.setter
    def center_x(self, new_x):
        self.center[0] = new_x

    @center_y.setter
    def center_y(self, new_y):
        self.center[1] = new_y

    @property
    def x(self):
        return self.center_x

    @x.setter
    def x(self, val):
        self.center_x = val

    @property
    def y(self):
        return self.center_y

    @y.setter
    def y(self, val):
        self.center_y = val

    @property
    def bbox(self):
        '''Returns sprite_types.Rect copy of bounding box'''
        surf_rect = self.get_rect()
        return sprite_types.Rect.from_rect(surf_rect)

    def get_rect(self):
        '''Returns pygame.Rect copy of bounding box'''
        return self.surf.get_rect(center=self.center)

    def get_surface(self):
        ''' Returns a copy of internal surface '''
        return self.surf.copy()

    def __repr__(self):
        return "{0.__class__}( center={0.center}, size={0.size})".format(self)

    def __copy__(self):
        return self.__cls__.from_surface(self.center, self.surf.copy())

    def save_to_disk(self, filename):
        pygame.image.save(self.get_surface(), filename)
        return True

    @property
    def size(self):
        return self.surf.get_size()

    @size.setter
    def size(self, value):
        raise NotImplementedError(
            "Setting size is ambiguous, so not implemented")

    @property
    def width(self):
        return self.get_width()

    @width.setter
    def width(self, value):
        raise NotImplementedError(
            "Setting width is ambiguous, so not implemented")

    @property
    def height(self):
        return self.get_height()

    @height.setter
    def height(self, value):
        raise NotImplementedError(
            "Setting height is ambiguous, so not implemented")

    @property
    def w(self):
        return self.width

    @w.setter
    def w(self, value):
        self.width = value

    @property
    def h(self):
        return self.height

    @h.setter
    def h(self, value):
        self.height = value

    # Surface methods not provided, apply on self.surf
    def blit(self, surface, (x, y)=(None, None), clip_rect=None):

        if (x, y) == (None, None):
            (x, y) = self.center
        # pygame.Rect does this, which is used in clear_rect
        x, y = int(x), int(y)

        if clip_rect:
            # Set clip region
            surface.set_clip(clip_rect)

        surface.blit(self.surf, self.bbox.topleft)

        if clip_rect:
            # Remove clip region
            surface.set_clip(None)

    def draw(self):
        tmp_dirty_sprite = {
            "sprite": self,
            # later shape may be used for matching insted of sprites, as sprite
            # may be subclassed, changing "sprite" obj
            "shape": "Img",
            "bbox": self.get_rect(),
            "config": {
                "center": self.center,
                "file_name_or_obj": self.surf
            }
        }
        for tmp_old_sprite in self.container_obj.list_rendered_sprite:
            # if any of them is eqv. to new dirty sprite, mark old one as
            # still_valid
            if tmp_old_sprite == tmp_dirty_sprite:
                # puts "still_valid" entry in the old_dirty_sprite_dict
                # quite good approach, wont match with anything again
                # but "still_valid" should be removed, before next step (tick), to allow
                # checks later
                tmp_old_sprite["still_valid"] = True
                break
        else:
            # i.e. if no match found, list it as dirty sprite
            self.container_obj.list_dirty_sprite.append(tmp_dirty_sprite)

    # collide func
    def collide(self, other_obj, moved_center=(None, None), from_container_obj=False):
        ''' Collision handler for any type of obj'''

        if not hasattr(other_obj, "__class__"):
            raise AttributeError(
                "other_obj must be a class or instance")

        # Handle instances
        if isinstance(other_obj, sprite_types.Circle):
            return self.collide_circle(other_obj, moved_center, from_container_obj)
        elif isinstance(other_obj, sprite_types.Rect):
            return self.collide_rect(other_obj, moved_center, from_container_obj)
        elif isinstance(other_obj, Img):
            # Todo: add img collision
            return self.collide_img(other_obj, moved_center, from_container_obj)
        else:
            pass

        # Handle classes
        if inspect.isclass(other_obj):
            if issubclass(other_obj,  sprite_types.Circle):
                return self.collide_list_circle(other_obj.list_instance, moved_center, from_container_obj)
            elif issubclass(other_obj, sprite_types.Rect):
                return self.collide_list_rect(other_obj.list_instance, moved_center, from_container_obj)
            elif issubclass(other_obj, Img):
                # Todo: add img collision
                return self.collide_list_img(other_obj.list_instance, moved_center, from_container_obj)
            else:
                pass

        # Handle fun_Game and fun_Class or their instances
        if isinstance(other_obj, base.fun_Game) or (inspect.isclass(other_obj) and issubclass(other_obj, base.fun_Game)):
            def instance_collide(other_obj):
                return self.collide(other_obj.sprite, moved_center, from_container_obj=True)
            to_return = other_obj.for_all_instance(
                instance_collide, flatten="level-class")
            remove_from_list(to_return, self.container_obj)
            remove_from_list(to_return, None, recursive=True)
            remove_from_list(to_return, False, recursive=True)
            return to_return
        if inspect.isclass(other_obj) and issubclass(other_obj, base.fun_Class):
            def instance_collide(other_obj):
                return self.collide(other_obj.sprite, moved_center, from_container_obj=True)
            to_return = other_obj.for_all_instance(
                instance_collide, flatten=False)
            remove_from_list(to_return, self.container_obj)
            remove_from_list(to_return, None, recursive=True)
            remove_from_list(to_return, False, recursive=True)
            return to_return
        if isinstance(other_obj, base.fun_Class):
            other_obj_sprite = other_obj.sprite
            to_return = self.collide(
                other_obj_sprite, moved_center, from_container_obj=True)
            # doesnt return list, so prev. case operations not done
            return to_return

    def collide_img(self, another_img, moved_center=(None, None), from_container_obj=False):
        ''' Checks if this img intersects with another_img. '''

        does_collide = self.collide(
            another_img.col_shape, moved_center, from_container_obj=False)
        if does_collide:
            if from_container_obj:
                to_return = another_img.container_obj
            else:
                to_return = another_img
        else:
            to_return = False
        return to_return

    def collide_list_img(self, list_img, moved_center=(None, None), from_container_obj=False):
        list_collided_img = []
        for each_img in list_img:
            if self.collide_img(each_img, moved_center, from_container_obj):
                # Collide_img itself takes container_obj into account, so no
                # need of post-processing
                list_collided_img.append(each_img)
        else:  # else of the for loop
            return False
        return list_collided_img

    def collide_circle(self, another_circle, moved_center=(None, None), from_container_obj=False):
        ''' Checks if this circle intersects with another_circle. '''
        if not (len(moved_center) == 2):
            raise AttributeError("moved_center must be of the form (x,y)")
        _center = self.center
        moved_center = list(moved_center)
        if moved_center[0] is None:
            moved_center[0] = _center[0]
        if moved_center[1] is None:
            moved_center[1] = _center[1]
        # temporarily move self to moved_center and shift back later
        self.center = moved_center
        does_collide = another_circle.collide_img(self)
        self.center = _center
        if does_collide:
            if from_container_obj:
                to_return = another_circle.container_obj
            else:
                to_return = another_circle
        else:
            to_return = False
        return to_return

    def collide_point(self, (point_x, point_y)):
        ''' Checks if (point_x, point_y) is inside this circle. '''
        does_collide = self.col_shape.collide_point((point_x, point_y))
        if does_collide:
            return True
        else:
            return False

    def collide_list_circle(self, list_circle, moved_center=(None, None), from_container_obj=False):
        ''' Returns list of circles in list_circle which intersects with this circle. '''
        list_collided_circle = []
        for circle in list_circle:
            if self.collide_circle(circle, moved_center, from_container_obj):
                # Collide_circle itself takes container_obj into account, so no
                # need of post-processing
                list_collided_circle.append(circle)
        else:  # else of the for loop
            return False
        return list_collided_circle

    def collide_list_any_circle(self, list_circle, moved_center=(None, None), from_container_obj=False):
        ''' Checks if this circle intersects with any of the circles in circle_list. '''
        for circle in list_circle:
            if self.collide_circle(circle, moved_center, from_container_obj):
                return True
        else:  # else of the for loop
            return False

    def collide_list_all_circle(self, list_circle, moved_center=(None, None), from_container_obj=False):
        ''' Checks if this circle intersects with all of the circles in circle_list. '''
        for circle in list_circle:
            if not self.collide_circle(circle, moved_center, from_container_obj):
                return False
        else:  # else of the for loop
            return True

    def collide_rect(self, another_rect, moved_center=(None, None), from_container_obj=False):
        ''' Checks if this circle collides with another_rect. '''
        if not (len(moved_center) == 2):
            print "moved_center must be of the form (x,y)"
        _center = self.center
        moved_center = list(moved_center)
        if moved_center[0] is None:
            moved_center[0] = _center[0]
        if moved_center[1] is None:
            moved_center[1] = _center[1]

        self.center = moved_center
        does_collide = another_rect.collide_img(self)
        self.center = _center
        if does_collide:
            if from_container_obj:
                to_return = another_rect.container_obj
            else:
                to_return = another_rect
        else:
            to_return = False
        return to_return

    def collide_list_rect(self, list_rect, moved_center=(None, None), from_container_obj=False):
        ''' Returns list of rect in list_rect which intersects with this circle. '''
        list_collided_rect = []
        for rect in list_rect:
            if self.collide_rect(rect, moved_center, from_container_obj):
                list_collided_rect.append(rect)
        else:  # else of the for loop
            return False
        return list_collided_rect

    def collide_list_any_rect(self, list_rect, moved_center=(None, None), from_container_obj=False):
        ''' Returns True if collides with any of the rect in the list. '''
        for temp_rect in list_rect:
            if self.collide_rect(self, temp_rect, moved_center, from_container_obj):
                return True
        else:
            return False

    def collide_list_all_rect(self, list_rect, moved_center=(None, None), from_container_obj=False):
        ''' Returns True if collides with all the rect in the list. '''
        for temp_rect in list_rect:
            if not self.collide_rect(self, temp_rect, moved_center, from_container_obj):
                return False
            else:
                return True
