#!/usr/bin/env python
# I, Danny Milosavljevic, hereby place this file into the public domain.

import pixbufs
import sprites
import palette

WIDTH = 256
HEIGHT = 240 # 224 visible on NTSC TV
COLUMN_COUNT = 32
ROW_COUNT = 30

# TODO double-height sprites are REALLY double-height (the data too). In order not to complicate the circuitry, the top half is taken from "an 8x8 sprite" at $0000, the bottom half at $1000.
class Screen(object): 
    def __init__(self):
        self.width = WIDTH
        self.height = HEIGHT
        self.PPU = None
        self.sprite_attributes = 256*[0]
        self.MMU = None
        self.current_scanline = (WIDTH + sprites.WIDTH * 2) * [0]
        self.raster_position = 0
        self.pixbuf_obj = pixbufs.Pixbuf()
        self.text_palette = [0,1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf]
        self.sprite_palette = [0,1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf]
        #self.pixbuf_obj.merge(5*self.width, self.width*[0xAFAFAFAF])
    def get_rendered_pixbuf(self):
        return(self.pixbuf_obj.native_pixbuf)
    def render_background(self):
        background_color_0 = palette.get_RGBA32_pixel(self.props.background_color_0)
        for i in range(WIDTH):
            self.current_scanline[i] = background_color_0
    def render_sprites(self, B_foreground):
        current_scanline = self.current_scanline
        sprite_pattern_address = self.props.sprite_pattern_address
        sprite_height = self.props.sprite_height
        for spriteI in range(sprites.SPRITE_COUNT):
            colorattr = self.sprite_attributes[spriteI*4 + 3]
            if ((colorattr&32)==0) != B_foreground:
                continue
            atts = self.sprite_attributes[spriteI*4 : (spriteI + 1)*4]
            # sprite attribute RAM: [Y,patnr,colorattr,X]
            colhigh = colorattr&3
            B_horizflip = colorattr&64
            B_vertflip = colorattr&128
            Y = atts[0]
            code = atts[1]
            X = atts[3]
            if self.raster_position < Y or self.raster_position >= Y + sprite_height:
                continue
            rowoff = self.raster_position - Y
            if rowoff >= 8: # double-height lower part of sprite
                # TODO test.
                self.copy_pattern(self.sprite_palette, sprite_pattern_address|0x1000, X, rowoff&7, code)
            else:
                self.copy_pattern(self.sprite_palette, sprite_pattern_address, X, rowoff&7, code)
    def render_scanline(self):
        props = self.props
        if not props.B_display_enabled:
            return
        self.render_background()
        self.render_sprites(False)
        index = self.raster_position
        #240 Mal (4 mal ' ')
        #40 Mal 0 # (for each 4x4 square)
        name_table_address = props.video_offset
        screen_pattern_address = props.screen_pattern_address
        rowix = index // 8
        rowoff = index & 7
        name_table_address += rowix*COLUMN_COUNT
        for colix in range(COLUMN_COUNT):
            code = self.MMU.read_memory(name_table_address + colix, 1)
            self.copy_pattern(self.text_palette, screen_pattern_address, colix*8, rowoff, code)
        self.render_sprites(True)
        self.flip()
    def copy_pattern(self, upalette, screen_pattern_address, X, rowoff, code):
        i = rowoff
        screen_pattern_address += 16*code
        if True: # for i in range(8):
            v0 = self.MMU.read_memory(screen_pattern_address + i, 1) # bits 0 of each pixel
            v1 = self.MMU.read_memory(screen_pattern_address + 8 + i, 1) # bits 1 of each pixel
            mask = 128
            for j in range(8):
                if (v0 & mask) != 0 or (v1 & mask) != 0: # TODO proper color
                    self.current_scanline[X + j] = palette.get_RGBA32_pixel(2) # FIXME upalette upalette[1]&0xF)
                mask >>= 1

    def increase_raster_position(self):
        self.render_scanline()
        self.raster_position += 1
        if self.raster_position >= HEIGHT:
            self.raster_position = 0
            self.reload()
    def reload(self):
        self.text_palette = [self.MMU.read_memory(0x3F00 + i, 1) for i in range(16)]
        self.sprite_palette = [self.MMU.read_memory(0x3F10 + i, 1) for i in range(16)]
    def flip(self):
        # actually put the finished line into the pixbuf.
        beginning_offset = WIDTH * self.raster_position
        self.pixbuf_obj.merge(beginning_offset, self.current_scanline)
    def set_PPU(self, value):
        self.PPU = value
        self.MMU = value.MMU
        self.props = value.props
    def unprepare(self):
        pass
    def set_sprite_attributes(self, values):
        # DMA
        sprite_attributes = self.sprite_attributes
        for i in range(256):
            sprite_attributes[i] = values[i]
    def get_sprite_attribute(self, addr):
        return self.sprite_attributes[addr]
    def set_sprite_attribute(self, addr, value):
        self.sprite_attributes[addr] = value
