# -*- coding: utf-8 -*-
"""MicroPython rotary encoder library.
Usage:
    from time import sleep_ms
    from encoder import Encoder
    # Connect CLK to D6, DT to D5, GND to GND and + to 3V3
    enc = Encoder(pin_clk=12, pin_dt=14)
    def readloop(e):
        oldval = 0
        while True:
            val = enc.value
            if oldval != val:
                print(val)
                oldval = val
            sleep_ms(50)
    readloop(e)

Source :
https://github.com/SpotlightKid/micropython-stm-lib/blob/master/encoder/encoder.py
"""

from machine import Pin

ENC_STATES = (
    0,   # 00 00
    -1,  # 00 01
    1,   # 00 10
    0,   # 00 11
    1,   # 01 00
    0,   # 01 01
    0,   # 01 10
    -1,  # 01 11
    -1,  # 10 00
    0,   # 10 01
    0,   # 10 10
    1,   # 10 11
    0,   # 11 00
    1,   # 11 01
    -1,  # 11 10
    0    # 11 11
)
ACCEL_THRESHOLD = const(5)


class Encoder(object):
    def __init__(self, pin_clk, pin_dt, pin_mode=None, clicks=1,
                 min_val=0, max_val=100, accel=0, reverse=False):
        self.pin_clk = (pin_clk if isinstance(pin_clk, Pin) else
                        Pin(pin_clk, Pin.IN, pin_mode))
        self.pin_dt = (pin_dt if isinstance(pin_dt, Pin) else
                       Pin(pin_dt, Pin.IN, pin_mode))

        self.min_val = min_val * clicks
        self.max_val = max_val * clicks
        self.accel = int((max_val - min_val) / 100 * accel)
        self.max_accel = int((max_val - min_val) / 2)
        self.clicks = clicks
        self.reverse = 1 if reverse else -1

        # The following variables are assigned to in the interrupt callback,
        # so we have to allocate them here.
        self._value = 0
        self._readings = 0
        self._state = 0
        self.cur_accel = 0

        self.set_callbacks(self._callback)

    def _callback(self, line):
        self._readings = (self._readings << 2 | self.pin_clk.value() << 1 |
                          self.pin_dt.value()) & 0x0f

        self._state = ENC_STATES[self._readings] * self.reverse

        if self._state:
            self.cur_accel = min(self.max_accel, self.cur_accel + self.accel)

            self._value = min(self.max_val, max(self.min_val, self._value +
                                                (1 + (self.cur_accel >> ACCEL_THRESHOLD)) *
                                                self._state))

    def set_callbacks(self, callback=None):
        mode = Pin.IRQ_RISING | Pin.IRQ_FALLING
        self.irq_clk = self.pin_clk.irq(trigger=mode, handler=callback)
        self.irq_dt = self.pin_dt.irq(trigger=mode, handler=callback)

    def close(self):
        self.set_callbacks(None)
        self.irq_clk = self.irq_dt = None

    @property
    def value(self):
        return self._value // self.clicks

    def reset(self, val=0):
        self._value = val


def test(enc=None, **kwargs):
    from time import sleep_ms
    rate = kwargs.pop('rate', 20)

    if not isinstance(enc, Encoder):
        cls = kwargs.pop('encoder_cls', Encoder)
        kwargs.setdefault('pin_clk', 12)
        kwargs.setdefault('pin_dt', 14)
        kwargs.setdefault('clicks', 4)
        enc = cls(**kwargs)

    oldval = 0
    try:
        while True:
            val = enc.value
            if oldval != val:
                print(val)
                oldval = val

            enc.cur_accel = max(0, enc.cur_accel - enc.accel)
            sleep_ms(1000 // rate)
    except:
        enc.close()
