from kmk.pins import PULL_UP


class RotaryEncoder:
    # Please don't ask. I don't know. All I know is bit_value
    # works as expected. Here be dragons, etc. etc.
    MIN_VALUE = False + 1 << 1 | True + 1
    MAX_VALUE = True + 1 << 1 | True + 1

    def __init__(self, pos_pin, neg_pin):
        self.pos_pin = pos_pin
        self.neg_pin = neg_pin

        self.pos_pin.switch_to_input(pull=PULL_UP)
        self.neg_pin.switch_to_input(pull=PULL_UP)

        self.prev_bit_value = self.bit_value()

    def value(self):
        return (self.pos_pin.value(), self.neg_pin.value())

    def bit_value(self):
        '''
        Returns 2, 3, 5, or 6 based on the state of the rotary encoder's two
        bits. This is a total hack but it does what we need pretty efficiently.
        Shrug.
        '''
        return self.pos_pin.value() + 1 << 1 | self.neg_pin.value() + 1

    def direction(self):
        '''
        Compares the current rotary position against the last seen position.

        Returns True if we're rotating "positively", False if we're rotating "negatively",
        and None if no change could safely be detected for any reason (usually this
        means the encoder itself did not change)
        '''
        new_value = self.bit_value()
        rolling_under = self.prev_bit_value == self.MIN_VALUE and new_value == self.MAX_VALUE
        rolling_over = self.prev_bit_value == self.MAX_VALUE and new_value == self.MIN_VALUE
        increasing = new_value > self.prev_bit_value
        decreasing = new_value < self.prev_bit_value
        self.prev_bit_value = new_value

        if rolling_over:
            return True
        elif rolling_under:
            return False

        if increasing:
            return True
        if decreasing:
            return False

        # Either no change, or not a type of change we can safely detect,
        # so safely do nothing
        return None