# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industries
#
# SPDX-License-Identifier: MIT
#
# This example is adapted in part from micropython:
# https://github.com/micropython/micropython/pull/6894/files

import array

import board
import rp2pio

import adafruit_pioasm


class IncrementalEncoder:
    _state_look_up_table = array.array(
        "b",
        [
            # Direction = 1
            0,  # 00 to 00
            -1,  # 00 to 01
            +1,  # 00 to 10
            +2,  # 00 to 11
            +1,  # 01 to 00
            0,  # 01 to 01
            +2,  # 01 to 10
            -1,  # 01 to 11
            -1,  # 10 to 00
            +2,  # 10 to 01
            0,  # 10 to 10
            +1,  # 10 to 11
            +2,  # 11 to 00
            +1,  # 11 to 01
            -1,  # 11 to 10
            0,  # 11 to 11
            # Direction = 0
            0,  # 00 to 00
            -1,  # 00 to 01
            +1,  # 00 to 10
            -2,  # 00 to 11
            +1,  # 01 to 00
            0,  # 01 to 01
            -2,  # 01 to 10
            -1,  # 01 to 11
            -1,  # 10 to 00
            -2,  # 10 to 01
            0,  # 10 to 10
            +1,  # 10 to 11
            -2,  # 11 to 00
            +1,  # 11 to 01
            -1,  # 11 to 10
            0,  # 11 to 11
        ],
    )

    _sm_code = adafruit_pioasm.assemble(
        """
    again:
        in pins, 2
        mov x, isr
        jmp x!=y, push_data
        mov isr, null
        jmp again
    push_data:
        push
        mov y, x
    """
    )

    _sm_init = adafruit_pioasm.assemble("set y 31")

    def __init__(self, pin_a, pin_b):
        if not rp2pio.pins_are_sequential([pin_a, pin_b]):
            raise ValueError("Pins must be sequential")

        self._sm = rp2pio.StateMachine(
            self._sm_code,
            160_000,
            init=self._sm_init,
            first_in_pin=pin_a,
            in_pin_count=2,
            pull_in_pin_up=0b11,
            in_shift_right=False,
        )

        self._counter = 0
        self._direction = 0
        self._lut_index = 0
        self._buffer = bytearray(1)

    def _update_state_machine(self, state):
        lut_index = self._lut_index | (state & 3)
        lut = self._state_look_up_table[lut_index]
        self._counter += lut
        if lut:
            self._direction = 1 if (lut > 0) else 0
        self._lut_index = ((lut_index << 2) & 0b1100) | (self._direction << 4)

    def deinit(self):
        self._sm.deinit()

    @property
    def value(self):
        while self._sm.in_waiting:
            self._sm.readinto(self._buffer)
            self._update_state_machine(self._buffer[0])
        return self._counter


encoder = IncrementalEncoder(board.GP2, board.GP3)

old_value = None
while True:
    value = encoder.value
    if old_value != value:
        print("Encoder:", value)
        old_value = value
