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

from chips import memory
import time
from c64 import joysticks
import iec3

# TODO translate
GDK_BUTTON1_MASK = 1 << 8
GDK_BUTTON2_MASK = 1 << 9
GDK_BUTTON3_MASK = 1 << 10

class KeyDevices(memory.Memory):
    # ShedSkin...
    def handle_key_press(self, name):
        return False
    def handle_key_release(self, name):
        return False
    def get_keyboard_matrix(self):
        return [["Return"]]
    def handle_mouse_motion(self, x, y, state):
        pass

# note that implementing read_memory is unnecessary!
# note that implementing write_memory is optional and should, if implemented, use MMU.IO_overlay_write_byte in order to tell the user of the new state.

class InputDevices(KeyDevices):
    def __init__(self, MMU, base_address, SID):
        memory.Memory.__init__(self)
        self.MMU = MMU
        self.base_address = base_address
        self.SID = SID
        self.A_data_direction = 0
        #self.B_data_direction = 0
        self.keyboard_matrix_rows = 0
        self.pressed_keys = set("dummy")
        self.pressed_keys.discard("dummy") # Shedskin hint...
        self.joysticks = [joysticks.DigitalJoystick(), joysticks.DigitalJoystick()]
        self.known_keys = set()
        self.paddle_state = 0
        for row in self.get_keyboard_matrix():
            for cell in row:
                self.known_keys.add(cell)
    matrix = [ # broken: 1 pound plus
        ["BackSpace", "Return", "Right",  "F7", "F1", "F3", "F5", "Down"],
        ["3", "W", "A", "4", "Z", "S", "E", "Shift_L"],
        ["5", "R", "D", "6", "C", "F", "T", "X"],
        ["7", "Y", "G", "8", "B", "H", "U", "V"],
        ["9", "I", "J", "0", "M", "K", "O", "N"],
        ["+", "P", "L", "-", ".", ":", "@", ","],
        ["pound", "*", ";", "Home", "Shift_R", "=", "grave", "/"],
        ["1", "LeftArrow", "Control_L", "2", "space", "Meta_L", "Q", "Break"],
    ]
    def write_memory(self, address, value, size):
        assert size == 1, "CIA.write_memory size is 1"
        if address == 0:
            self.keyboard_matrix_rows = ~(value & 0xFF)
            # the topmost 2 bits select which of the paddles to read (from the joystick ports). The value shows up in the SID then.
            self.update_IO_memory()
        elif address == 1: # note that the timer can twiddle bit 7 here and so you want to be able to read/write memory.
            pass
        elif address == 2:
            self.A_data_direction = value
            self.update_IO_memory()
        elif address == 3:
            pass
            #self.B_data_direction = value
        else:
            print(hex(address))
            assert False, "CIA address is known"
    def get_second_state(self):
            result = 0xFF
            joystick = self.joysticks[1]
            if joystick.B_up:
                result &=~ 1
            if joystick.B_down:
                result &=~ 2
            if joystick.B_left:
                result &=~ 4
            if joystick.B_right:
                result &=~ 8
            if joystick.B_fire_1:
                result &=~ 16
            if (self.paddle_state & GDK_BUTTON1_MASK) != 0:
                result &=~ 16
            # TODO: support other buttons
            # TODO: artificially make Up and Left work.
            keyboard_matrix_rows = self.keyboard_matrix_rows & self.A_data_direction
            if keyboard_matrix_rows != 0: # if you just want the joystick, set keyboard_matrix_rows = 0
                for row in range(0, 8):
                    if (keyboard_matrix_rows & (1 << row)) != 0: # client wants to know
                        columns = InputDevices.matrix[row]
                        #print("possible", rows)
                        for column_i, cell in enumerate(columns):
                            if cell in self.pressed_keys: # or (isinstance(cell, int) and cell < 128 and (cell | 0x20) in self.pressed_keys):
                                #print("YESSS, matched", cell)
                                result &=~ (1 << column_i)
            return result
    def update_IO_memory(self):
        #if (self.A_data_direction & 31) == 0: # sets the joystick bits to "read". TODO could also be just one of them?
        joystick = self.joysticks[0]
        state0 = (0 if joystick.B_up else 1) | \
                   (0 if joystick.B_down else 2) | \
                   (0 if joystick.B_left else 4) | \
                   (0 if joystick.B_right else 8) | \
                   (0 if joystick.B_fire_1 else 16) | \
                   32 | \
                   64
        state1 = self.get_second_state()
        self.MMU.IO_overlay_write_byte(self.base_address + 0, state0)
        self.MMU.IO_overlay_write_byte(self.base_address + 1, state1)
    def handle_key_press(self, name):
        if name not in self.pressed_keys:
            if name == "Down":
                self.joysticks[0].B_down = True
            elif name == "Up":
                self.joysticks[0].B_up = True
            elif name == "Left":
                self.joysticks[0].B_left = True
            elif name == "Right":
                self.joysticks[0].B_right = True
            elif name == "Shift_R":
                self.joysticks[0].B_fire_1 = True
            self.pressed_keys.add(name)
        self.update_IO_memory()
        return name in self.known_keys
    def handle_key_release(self, name):
        if name in self.pressed_keys:
            if name == "Down":
                self.joysticks[0].B_down = False
            elif name == "Up":
                self.joysticks[0].B_up = False
            elif name == "Left":
                self.joysticks[0].B_left = False
            elif name == "Right":
                self.joysticks[0].B_right = False
            elif name == "Shift_R":
                self.joysticks[0].B_fire_1 = False
            self.pressed_keys.discard(name)
        self.update_IO_memory()
        return name in self.known_keys
    def handle_mouse_motion(self, x, y, state):
        print("mouse motion")
        # if we want to support 2 mouses (we don't), we would have to check the top 2 bits of the CIA2 keyboard row slot here.
        self.SID.set_paddle_state(x, y)
        self.paddle_state = state
        self.update_IO_memory()
    def get_keyboard_matrix(self):
        return InputDevices.matrix

class SerialDevices(KeyDevices):
    def __init__(self, MMU, base_address, IEC):
        memory.Memory.__init__(self)
        self.MMU = MMU
        self.base_address = base_address
        self.IEC = IEC
        self.A_data = 0
        self.A_data_direction = 0
    def read_memory(self, address, size):
        if address == 0: # CIA 2 IEC
            self.IEC.poke()
        return(0xFF)
    def write_memory(self, address, value, size):
        assert size == 1, "CIA.write_memory size is 1"
        if address == 0:
            self.A_data = value
            self.IEC.set_control_mask(self.A_data & self.A_data_direction) # output bit set: line is low. Reverse for input.
        elif address == 2:
            self.A_data_direction = value
            self.IEC.set_control_mask(self.A_data & self.A_data_direction) # output bit set: line is low. Reverse for input.
    def get_keyboard_matrix(self):
        return [["None"]]
    # see IEC      self.MMU.IO_overlay_write_byte(self.base_address + 0, state0)
    # see IEC self.MMU.IO_overlay_write_byte(self.base_address + 1, state1)

class D1541SerialDevices(memory.Memory):
    def __init__(self, MMU, base_address, IEC):
        memory.Memory.__init__(self)
        self.IEC = IEC
        self.A_data = 0
        self.A_data_direction = 0
    def write_memory(self, address, value, size):
        assert size == 1, "VIA.write_memory size is 1"
        if address == 0:
            self.A_data = value
            self.IEC.set_control_mask(self.A_data & self.A_data_direction)
        elif address == 2:
            self.A_data_direction = value
            self.IEC.set_control_mask(self.A_data & self.A_data_direction)

class PhysicalDrive(memory.Memory):
    def __init__(self, MMU, base_address):
        memory.Memory.__init__(self)
        self.MMU = MMU
        self.base_address = base_address
    def write_memory(self, address, value, size):
        assert size == 1, "VIA.write_memory size is 1"
        if address == 0:
            self.A_data = value
            # bit 0..1: cycling bits will move the head: 00/01/10/11/00 will move head in; 00/11/10/01/00 will move head out.
            # bit 2: disk change indicator (toggles). Drive motor if set.
            # bit 3: set=led is ON.
            # bit 4: writable (not write protected) (IN).
            # bit 5..6: data density.
            # bit 7: not-sync-area (IN).
    #self.MMU.IO_overlay_write_byte(self.base_address + 0, state0)
