DBG = 0

# digital inputs
PIN_CLK = 1
PIN_RESET = 2
# digital outputs
PIN_ACK = 9
PIN_COMMAND_OK = 10
PIN_EXECUTING_COMMAND = 11
PIN_RETURN_VAL = 12

# interface commands
COMMAND_MOVE = 0
COMMAND_ROTATE = 1
COMMAND_PICK_UP = 2
COMMAND_PUT_DOWN = 3
COMMAND_CHANGE_NIXIE_TYPE = 4
COMMAND_START_CALIBRATION = 5
COMMAND_STOP_CALIBRATION = 6
COMMAND_CHANGE_CALIBRATION = 7
NUM_OF_COMMANDS = 8

COMMAND_LENGTH = [3, 2, 1, 1, 2, 3, 1, 3]

# interface states
INTERFACE_STATE_IDLE = 0
INTERFACE_STATE_READ_BYTE = 1
INTERFACE_STATE_WAITING_FOR_CLK_LOW = 2
INTERFACE_STATE_WAITING_FOR_NEXT_BYTE = 3
INTERFACE_STATE_WAITING_FOR_EXECUTION = 4
INTERFACE_STATE_RESET = 5

class PLCInterface(object):
    def __init__(self, controller, **kwargs):
        self._controller = controller
        self._interfaceState = INTERFACE_STATE_IDLE
        self._prevResetState = 0
        self._inputBuffer = []
        self._commandReceived = 0
        self._interfaceStateAfterClkLow = INTERFACE_STATE_IDLE

    def _transfer_success(self):
        if (DBG):
            print("interface transfer success")
        self._controller.digitalWrite(PIN_COMMAND_OK, 1)
        self._controller.digitalWrite(PIN_EXECUTING_COMMAND, 1)
        self._commandReceived = 1

    def _transfer_error(self):
        if (DBG):
            print("interface transfer error")
        self._controller.digitalWrite(PIN_COMMAND_OK, 0)
        self._controller.digitalWrite(PIN_EXECUTING_COMMAND, 0)
        self._commandReceived = 0

    def getCommand(self):
        return self._inputBuffer

    def commandReceived(self):
        return self._interfaceState == INTERFACE_STATE_WAITING_FOR_EXECUTION and self._commandReceived
        
    def executingCommand(self):
        return self._interfaceState == INTERFACE_STATE_WAITING_FOR_EXECUTION
    
    def ackCommandReceived(self):
        self._commandReceived = 0

    def commandExecuted(self, retVal = 0):
        if (DBG):
            print("interface command executed")
        self._controller.digitalWrite(PIN_RETURN_VAL, retVal)
        # maybe need delay
        self._controller.digitalWrite(PIN_EXECUTING_COMMAND, 0)
        self._interfaceState = INTERFACE_STATE_IDLE

    def reset(self):
        if (DBG):
            print("interface reset")
        self._inputBuffer.clear()
        self._controller.digitalWrite(PIN_COMMAND_OK, 0)
        self._controller.digitalWrite(PIN_EXECUTING_COMMAND, 0)
        self._controller.digitalWrite(PIN_ACK, 1)
        self._commandReceived = 0
        self._interfaceState = INTERFACE_STATE_RESET

    def handle(self):
        # reset input rising edge detection
        resetState = self._controller.digitalRead(PIN_RESET)
        if resetState and not self._prevResetState:
            self.reset()
        self._prevResetState = resetState
        
        # interface state machine
        if self._interfaceState == INTERFACE_STATE_IDLE:
            if self._controller.digitalRead(PIN_CLK):
                if (DBG):
                    print("interface transfer start")
                self._inputBuffer.clear()
                self._controller.digitalWrite(PIN_COMMAND_OK, 0)
                self._controller.digitalWrite(PIN_EXECUTING_COMMAND, 0)
                self._interfaceState = INTERFACE_STATE_READ_BYTE

        elif self._interfaceState == INTERFACE_STATE_READ_BYTE:
            # read byte from GPIO
            byte = 0
            for i in range(0,8):
                byte |= self._controller.digitalRead(8+i) << i
            if (DBG):
                print("interface received byte: " + str(byte))
            self._inputBuffer.append(byte)

            # detect if it is the last byte of the command
            if self._inputBuffer[0] >= NUM_OF_COMMANDS:
                # error
                self._transfer_error()
                self._interfaceStateAfterClkLow = INTERFACE_STATE_IDLE

            elif len(self._inputBuffer) >= COMMAND_LENGTH[self._inputBuffer[0]]:
                # last byte received
                self._transfer_success()
                self._interfaceStateAfterClkLow = INTERFACE_STATE_WAITING_FOR_EXECUTION

            else:
                # anticipating more bytes
                self._interfaceStateAfterClkLow = INTERFACE_STATE_WAITING_FOR_NEXT_BYTE
                if (DBG):
                    print("interface waiting for next byte")

            # ack byte received
            self._controller.digitalWrite(PIN_ACK, 1)
            self._interfaceState = INTERFACE_STATE_WAITING_FOR_CLK_LOW

        elif self._interfaceState == INTERFACE_STATE_WAITING_FOR_CLK_LOW:
            if not self._controller.digitalRead(PIN_CLK):
                if (DBG):
                    print("interface clk low")
                self._interfaceState = self._interfaceStateAfterClkLow
                self._controller.digitalWrite(PIN_ACK, 0)

        elif self._interfaceState == INTERFACE_STATE_WAITING_FOR_NEXT_BYTE:
            if self._controller.digitalRead(PIN_CLK):
                self._interfaceState = INTERFACE_STATE_READ_BYTE

        elif self._interfaceState == INTERFACE_STATE_WAITING_FOR_EXECUTION:
            # hang in here until the arm finishes executing the command
            pass

        elif self._interfaceState == INTERFACE_STATE_RESET:
            if not self._controller.digitalRead(PIN_RESET):
                if (DBG):
                    print("interface reset done")
                self._controller.digitalWrite(PIN_ACK, 0)
                self._interfaceState = INTERFACE_STATE_IDLE
