# -*- coding: utf-8 -*-

import sys
from struct import unpack
from interpreter import Interpreter


class Chip8:
    def __init__(self, rom_path):
        try:
            self.rom = open(rom_path, "r+b")
        except IOError, error:
            print error
            sys.exit()

        self._memory_address = 0
        self._fontset_address = 0x50
        self._program_address = 0x200

        self.interpreter = Interpreter()

    def set_memory_address(self, value):
        self._memory_address = value

    def get_memory_address(self):
        return self._memory_address

    def get_fontset_address(self):
        return self._fontset_address

    def get_program_address(self):
        return self._program_address

    def _load_fontset(self):
        """Loads fontset into memory."""
        self.set_memory_address(self.get_fontset_address())  # Fontset address
        fontset = (
            0xF0, 0x90, 0x90, 0x90, 0xF0,  # 0
            0x20, 0x60, 0x20, 0x20, 0x70,  # 1
            0xF0, 0x10, 0xF0, 0x80, 0xF0,  # 2
            0xF0, 0x10, 0xF0, 0x10, 0xF0,  # 3
            0x90, 0x90, 0xF0, 0x10, 0x10,  # 4
            0xF0, 0x80, 0xF0, 0x10, 0xF0,  # 5
            0xF0, 0x80, 0xF0, 0x90, 0xF0,  # 6
            0xF0, 0x10, 0x20, 0x40, 0x40,  # 7
            0xF0, 0x90, 0xF0, 0x90, 0xF0,  # 8
            0xF0, 0x90, 0xF0, 0x10, 0xF0,  # 9
            0xF0, 0x90, 0xF0, 0x90, 0x90,  # A
            0xE0, 0x09, 0xE0, 0x90, 0xE0,  # B
            0xF0, 0x80, 0x80, 0x80, 0xF0,  # C
            0xE0, 0x90, 0x90, 0x90, 0xE0,  # D
            0xF0, 0x80, 0xF0, 0x80, 0xF0,  # E
            0xF0, 0x80, 0xF0, 0x80, 0x80   # F
        )
        for font_data in fontset:
            self.interpreter.set_memory(self.get_memory_address(), font_data)
            self.set_memory_address(self.get_memory_address() + 1)

    def _load_rom(self):
        """Loads ROM into memory."""
        self.set_memory_address(self.get_program_address())  # Program Address
        while True:
            data = self.rom.read(1)
            if not data:
                break
            self.interpreter.set_memory(self.get_memory_address(), unpack('B', data)[0])
            self.set_memory_address(self.get_memory_address() + 1)
        self.rom.close()

    def initialize(self):
        """Iinitializes Chip-8"""
        self._load_fontset()
        self._load_rom()
        self.interpreter.set_pc(self.get_program_address())  # Set the program counter to program_address

if __name__ == "__main__":
    chip8 = Chip8("./ROMS/INVADERS")
    chip8.initialize()
    chip8.interpreter.interpret()