"""Square tile based engine."""

from pgu.vid import *
import pygame


class Tilevid(Vid):
    """Based on [[vid]] -- see for reference."""

    def paint(self, s):
        sw, sh = s.get_width(), s.get_height()
        self.view.w, self.view.h = sw, sh

        tiles = self.tiles
        tw, th = tiles[0].image.get_width(), tiles[0].image.get_height()
        w, h = self.size

        if self.bounds != None:
            self.view.clamp_ip(self.bounds)

        ox, oy = self.view.x, self.view.y
        tlayer = self.tlayer
        blayer = self.blayer
        alayer = self.alayer
        sprites = self.sprites

        blit = s.blit
        yy = -(self.view.y % th)
        my = (oy + sh) / th
        if (oy + sh) % th:
            my += 1

        if blayer != None:
            for y in range(oy / th, my):
                if y >= 0 and y < h:
                    trow = tlayer[y]
                    brow = blayer[y]
                    arow = alayer[y]
                    xx = -(self.view.x % tw)
                    mx = (ox + sw) / tw
                    # if (ox+sh)%tw: mx += 1
                    for x in range(ox / tw, mx + 1):
                        if x >= 0 and x < w:
                            blit(tiles[brow[x]].image, (xx, yy))
                            blit(tiles[trow[x]].image, (xx, yy))
                            arow[x] = 0
                        xx += tw
                yy += th
        else:
            for y in range(oy / th, my):
                if y >= 0 and y < h:
                    trow = tlayer[y]
                    arow = alayer[y]
                    xx = -(self.view.x % tw)
                    mx = (ox + sw) / tw
                    # if (ox+sh)%tw: mx += 1
                    for x in range(ox / tw, mx + 1):
                        if x >= 0 and x < w:
                            blit(tiles[trow[x]].image, (xx, yy))
                            arow[x] = 0
                        xx += tw
                yy += th

        for s in sprites:
            s.irect.x = s.rect.x - s.shape.x
            s.irect.y = s.rect.y - s.shape.y
            blit(s.image, (s.irect.x - ox, s.irect.y - oy))
            s.updated = 0
            s._irect = Rect(s.irect)
            # s._rect = Rect(s.rect)

        self.updates = []
        self._view = pygame.Rect(self.view)
        return [Rect(0, 0, sw, sh)]

    def update(self, s):
        sw, sh = s.get_width(), s.get_height()
        self.view.w, self.view.h = sw, sh

        if self.bounds != None:
            self.view.clamp_ip(self.bounds)
        if self.view.x != self._view.x or self.view.y != self._view.y:
            return self.paint(s)

        ox, oy = self.view.x, self.view.y
        sw, sh = s.get_width(), s.get_height()
        w, h = self.size
        tlayer = self.tlayer
        blayer = self.blayer
        alayer = self.alayer
        tiles = self.tiles
        tw, th = tiles[0].image.get_width(), tiles[0].image.get_height()
        sprites = self.sprites
        blit = s.blit

        us = []

        # mark places where sprites have moved, or been removed

        ss = self.sprites.removed
        self.sprites.removed = []
        ss.extend(sprites)
        for s in ss:
            # figure out what has been updated.
            s.irect.x = s.rect.x - s.shape.x
            s.irect.y = s.rect.y - s.shape.y
            if (
                s.irect.x != s._irect.x
                or s.irect.y != s._irect.y
                or s.image != s._image
            ):
                # w,h can be skipped, image covers that...
                s.updated = 1
            if s.updated:
                r = s._irect
                y = max(0, r.y / th)
                yy = min(h, r.bottom / th + 1)
                while y < yy:
                    x = max(0, r.x / tw)
                    xx = min(w, r.right / tw + 1)
                    while x < xx:
                        if alayer[y][x] == 0:
                            self.updates.append((x, y))
                        alayer[y][x] = 1
                        x += 1
                    y += 1

                r = s.irect
                y = max(0, r.y / th)
                yy = min(h, r.bottom / th + 1)
                while y < yy:
                    x = r.x / tw
                    xx = min(w, r.right / tw + 1)
                    while x < xx:
                        if alayer[y][x] == 0:
                            alayer[y][x] = 2
                            self.updates.append((x, y))
                        x += 1
                    y += 1

        # mark sprites that are not being updated that need to be updated because
        # they are being overwritte by sprites / tiles
        for s in sprites:
            if s.updated == 0:
                r = s.irect
                y = max(0, r.y / th)
                yy = min(h, r.bottom / th + 1)
                while y < yy:
                    x = max(0, r.x / tw)
                    xx = min(w, r.right / tw + 1)
                    while x < xx:
                        if alayer[y][x] == 1:
                            s.updated = 1
                        x += 1
                    y += 1

        for u in self.updates:
            x, y = u
            xx, yy = x * tw - ox, y * th - oy
            if alayer[y][x] == 1:
                if blayer != None:
                    blit(tiles[blayer[y][x]].image, (xx, yy))
                blit(tiles[tlayer[y][x]].image, (xx, yy))
            alayer[y][x] = 0
            us.append(Rect(xx, yy, tw, th))

        for s in sprites:
            if s.updated:
                blit(s.image, (s.irect.x - ox, s.irect.y - oy))
                s.updated = 0
                s._irect = Rect(s.irect)
                s._image = s.image

        self.updates = []
        return us

    def view_to_tile(self, pos):
        x, y = pos
        tiles = self.tiles
        tw, th = tiles[0].image.get_width(), tiles[0].image.get_height()
        return x / tw, y / th

    def tile_to_view(self, pos):
        x, y = pos
        tiles = self.tiles
        tw, th = tiles[0].image.get_width(), tiles[0].image.get_height()
        x, y = x * tw, y * th
        return x, y

    def screen_to_tile(self, pos):
        x, y = pos
        x, y = x + self.view.x, y + self.view.y
        return self.view_to_tile((x, y))

    def tile_to_screen(self, pos):
        x, y = pos
        x, y = self.tile_to_view(pos)
        x, y = x - self.view.x, y - self.view.y
        return x, y


# vim: set filetype=python sts=4 sw=4 noet si :
