import os

from PIL import Image, ImageFile

DEFAULT_PALETTE = "classic"
PALETTES = {
    "classic": [0x9B, 0xBC, 0x0F, 0x30, 0x62, 0x30, 0x8B, 0xAC, 0x0F, 0x0F, 0x38, 0x0F],
    "blue": [0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x63, 0xA5, 0xFF, 0x00, 0x00, 0x00],
    "brown": [0xFF, 0xFF, 0xFF, 0x84, 0x31, 0x00, 0xFF, 0xAD, 0x63, 0x00, 0x00, 0x00],
    "dark_blue": [0xFF, 0xFF, 0xFF, 0x52, 0x52, 0x8C, 0x8C, 0x8C, 0xDE, 0x00, 0x00, 0x00],
    "dark_brown": [0xFF, 0xE6, 0xC5, 0x84, 0x6B, 0x29, 0xCE, 0x9C, 0x84, 0x5A, 0x31, 0x08],
    "dark_green": [0xFF, 0xFF, 0xFF, 0x00, 0x63, 0xC5, 0x7B, 0xFF, 0x31, 0x00, 0x00, 0x00],
    "grayscale": [0xFF, 0xFF, 0xFF, 0x52, 0x52, 0x52, 0xA5, 0xA5, 0xA5, 0x00, 0x00, 0x00],
    "green": [0xFF, 0xFF, 0xFF, 0x52, 0x94, 0x00, 0x52, 0xFF, 0x00, 0x00, 0x00, 0x00],
    "inverted": [0x00, 0x00, 0x00, 0xFF, 0xDE, 0x00, 0x00, 0x84, 0x84, 0xFF, 0xFF, 0xFF],
    "orange": [0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00],
    "pastel_mix": [0xFF, 0xFF, 0xA5, 0x94, 0x94, 0xFF, 0xFF, 0x94, 0x94, 0x00, 0x00, 0x00],
    "red": [0xFF, 0xFF, 0xFF, 0x94, 0x3A, 0x3A, 0xFF, 0x84, 0x84, 0x00, 0x00, 0x00],
    "yellow": [0xFF, 0xFF, 0xFF, 0x7B, 0x4A, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00],
}


class GBCamImageFile(ImageFile.ImageFile):
    format = "GBCamera"
    format_description = "Game Boy Camera save file"
    _close_exclusive_fp_after_loading = False

    __fp = None
    __frame = 0
    _n_frames = 30

    def _open(self):
        self.fp.seek(0, os.SEEK_END)
        if self.fp.tell() != 131072:
            raise SyntaxError("GB Camera save file must be 128K")

        self.mode = "P"
        self.size = (16 * 8, 14 * 8)

        # Apparently ImageFile does 'self.fp = None' after loading, so we must keep a reference to it.
        self.__fp = self.fp
        self.seek(0)

        self.putpalette(PALETTES[DEFAULT_PALETTE])

    def load_read(self, n):
        res = b''

        # GB tiles are described here: https://github.com/jansegre/gameboy/blob/master/spec/gbspec.txt#L274
        data = self.fp.read(16)
        for y in range(8):
            for x in reversed(range(8)):
                c = ((data[y * 2] >> x) & 1) * 2 + ((data[y * 2 + 1] >> x) & 1)
                res += bytes([c])

        return res

    def seek(self, frame):
        if frame >= self._n_frames:
            raise EOFError

        self.__frame = frame
        self.fp = self.__fp

        # GBCamera pictures start at 0x2000, and have the following structure:
        #  * Large photo (16x14 tiles, 0xe00 bytes)
        #  * Small photo (4x4 tiles, 0x100 bytes)
        #  * Photo info (0x100 bytes)
        # More info here: http://www.devrs.com/gb/files/gbcam.txt
        self.tile = []
        for t in range(16 * 14):
            x = (t % 16) * 8
            y = (t // 16) * 8
            self.tile.append(("raw", (x, y, x + 8, y + 8), 0x2000 + 0x1000 * self.__frame + 16 * t, (self.mode, 0, 1)))

    def tell(self):
        return self.__frame

Image.register_open("GBCamera", GBCamImageFile)
Image.register_extension("GBCamera", ".sav")
