# coding=utf-8
from map_stuff import MapData
from pygame import Surface
from pygame import Rect
from windows import *
import pygame

__author__ = 'Kodex'

# -Settings

# --Dialog window
dialog_line_space_ = 25


def render_text_on_surface(surface, font=None, text='def', color=(255, 255, 255), bg_color=(0, 0, 0), position=(0, 0)):
    if font is None:
        return surface
    new_render = font.render(text, False, color, bg_color)

    surface.blit(new_render, position)

    return surface


def render_window(dest_surface, window_inst, default_font):
    """

    :param dest_surface: pygame.Surface
    :type window_inst: Window or DialogWindow
    :rtype: surface
    """
    # Luodaan rect ja surface ikkunalle.
    surface_rect = window_inst.rect
    surface = pygame.Surface(surface_rect.size)

    bg_color, bg_image = window_inst.get_background

    surface.fill(bg_color)
    if bg_image:
        surface.blit(bg_image, (0, 0))
    # -- Frames --
    print surface_rect.topleft
    surface.blit(window_inst.get_frame_surface, (0, 0))

    # -- Dialog --
    try:
        text_lines = window_inst.get_text_lines
        title = window_inst.get_title

        color = (255, 255, 255)
        bg_color = (0, 0, 0)

        text_pos = (30, 40)
        for line in text_lines:
            text_pos = text_pos[0], text_pos[1] + dialog_line_space_
            if line.__len__() != 0:
                render_text_on_surface(surface, default_font, line, position=text_pos, color=color, bg_color=bg_color)

        text_pos = (20, 15)
        render_text_on_surface(surface, default_font, title, position=text_pos, color=color, bg_color=bg_color)

    except NameError, error:
        print error

    dest_surface.blit(surface, surface_rect)

    return dest_surface


def render_frames(frame, surface=None, size=None):
    """
    Dynamically render frames inside surface.
    :type surface: pygame.Surface
    :type frame: list or pygame.Surface
    :type size (int)
    """
    dest_surface = surface
    if surface is None:
        if size is None:
            return None
        dest_surface = pygame.Surface(size)

    surface_size = dest_surface.get_size()
    try:
        frame_size = frame[0].get_size()
        # Coordinates:
        top = 0
        bot = surface_size[1] - frame_size[1]
        left = 0
        right = surface_size[0] - frame_size[0]
        # Sides
        for x in range(0, surface_size[0] - 1):
            dest_surface.blit(frame[1], (x, top))
            dest_surface.blit(frame[5], (x, bot))
        for y in range(0, surface_size[1] - 1):
            dest_surface.blit(frame[3], (left, y))
            dest_surface.blit(frame[7], (right, y))
        # Corners
        dest_surface.blit(frame[0], (left, top))
        dest_surface.blit(frame[2], (right, top))
        dest_surface.blit(frame[4], (right, bot))
        dest_surface.blit(frame[6], (left, bot))
    except TypeError, err:
        if surface_size != frame.get_size():
            frame = pygame.transform.scale(frame, surface_size)
        dest_surface.blit(frame, (0, 0))

    return dest_surface


class DirtyDrawing:
    world_surface_redraw = None

    dirty_tiles = []

    def __init__(self, floor_surface, map_data_texture_layer):
        assert floor_surface is not None
        assert map_data_texture_layer is not None

        self.prepare_floor(floor_surface, map_data_texture_layer)

        self.world_surface_redraw = True

    def draw(self, screen, world_s, camera, floor_s, map_data):
        """
        :param camera: Camera-class instance
        :param world_s: The world surface
        :param screen: The screen surface
        :type floor_s: Surface
        :type map_data: MapData
        """

        # Redraw the whole world window
        if self.world_surface_redraw:
            self.redraw_world_surface(screen, world_s, camera, floor_s, map_data)

        # Draw only the changed parts of the world window
        else:
            self.update_world_window(screen, world_s, camera, floor_s, map_data)

    def issue_world_surface_redraw(self):
        self.world_surface_redraw = True

    def redraw_world_surface(self, screen, world_s, camera, floor_s, map_data):
        # Blit the floor surface to world_surface.
        """

        :param map_data: The map_data instance
        :param floor_s: The floor surface
        :param world_s: The world surface
        :param screen: The screen surface
        :type camera: Camera
        """
        world_s.blit(floor_s, (0, 0))
        # Loop items on map
        for tile_position, item_or_list in map_data.get_item_layer.items():
            position_on_world = world_pos_of_tile(tile_position)
            self.draw_items_on_tile(world_s, item_or_list, position_on_world)
        # Loop characters on map
        for entity in map_data.get_characters_on_map:
            world_s.blit(entity.surface, entity.rect.topleft)

        # Blit the world surface to screen
        camera_rectangle = camera.get_viewport_rect
        assert isinstance(camera_rectangle, Rect)
        screen.blit(world_s, (16, 16), area=world_rect_of_tile_rect(camera_rectangle))

        self.world_surface_redraw = False
        map_data.reset_dirty_tiles()

    def update_world_window(self, screen, world_s, camera, floor_s, map_data):
        blit_area = Rect(0, 0, 64, 64)
        # Get the dirty tile -coordinates from map_data
        for dirty_tile_position in map_data.get_dirty_tile_positions:
            # Calculate the screen position for given tile
            dirty_world_position = world_pos_of_tile(dirty_tile_position)

            area_x, area_y = dirty_tile_position
            blit_area.topleft = (area_x * 64, area_y * 64)

            # Blit the tile from floor, to screen.
            world_s.blit(floor_s, dirty_world_position, blit_area)
            # Blit items in the tile

            # Try if this position contains a list of items. Note: these may fail :s
            items_in_position = map_data.get_items_on_position(dirty_tile_position)
            if items_in_position is not None:
                self.draw_items_on_tile(world_s, items_in_position, dirty_world_position)

            # Blit character in here.
            character = map_data.get_character_on_position(dirty_tile_position)
            if character is not None:
                world_s.blit(character.surface, dirty_world_position)

            camera_rectangle = camera.get_viewport_rect
            assert isinstance(camera_rectangle, Rect)
            screen.blit(world_s, (16, 16), area=world_rect_of_tile_rect(camera_rectangle))

        map_data.reset_dirty_tiles()

    @staticmethod
    def draw_items_on_tile(screen, item_or_list, screen_position):
        try:
            for item in item_or_list:
                item_surface = item.surface
                if item_surface is not None:
                    screen.blit(item.surface, screen_position)
        # There's a single item. - (item object not iterable)
        except TypeError:
            item_surface = item_or_list.surface
            if item_surface is not None:
                screen.blit(item_surface, screen_position)

    @staticmethod
    def prepare_floor(floor_s, texture_layer):
        """
        :type floor_s: Surface
        :type texture_layer: dict
        """

        for position, texture in texture_layer.items():
            x_pos, y_pos = position[0] * 64, position[1] * 64
            floor_s.blit(texture, (x_pos, y_pos))

    @staticmethod
    def draw_message_log(screen, message_log):
        screen.blit(message_log.render, message_log.position)
        message_log.set_is_dirty(False)


def screen_position_of_tile(tile_position):
    """

    :param tile_position: tile coordinate tuple
    :rtype : tuple
    """
    return tile_position[0] * 64 + 16, tile_position[1] * 64 + 16


def world_pos_of_tile(tile_position):
    return tile_position[0] * 64, tile_position[1] * 64


def world_rect_of_tile_rect(tile_rectangle):
    assert tile_rectangle, Rect
    topleft = tile_rectangle.topleft
    topleft = topleft[0] * 64, topleft[1] * 64
    width = tile_rectangle.width * 64
    height = tile_rectangle.height * 64
    world_rect = Rect(topleft, (width, height))
    return world_rect
