"""
Source: https://bitbucket.org/thesheep/micropython-d1motor

import d1motor
from machine import I2C, Pin
i2c = I2C(Pin(5), Pin(4), freq=100000)
m0 = d1motor.Motor(0, i2c)
m1 = d1motor.Motor(1, i2c)
m0.speed(5000)

"""
import ustruct
import utime
import math

sign = lambda x: math.copysign(1, x)


_STATE_BRAKE = const(0)
_STATE_RIGHT = const(1) # clockwise
_STATE_LEFT = const(2) # counter-clockwise
_STATE_STOP = const(3)
_STATE_SLEEP = const(4)


class Motor:
    def __init__(self, index, i2c, address=0x30, standby_pin=None):
        if index not in (0, 1):
            raise ValueError("Index must be 0 or 1")
        self.index = index
        self.i2c = i2c
        self.address = address
        self.standby_pin = standby_pin
        self._speed = 0
        self._state = 0
        if standby_pin is not None:
            standby_pin.init(standby_pin.OUT, 0)
        self.frequency(1000)

    def frequency(self, frequency=None):
        if frequency is None:
            return self._pwm_frequency
        self._pwm_frequency = frequency
        self.i2c.writeto_mem(self.address, 0x00 | self.index,
            ustruct.pack(">BH", 0x00, frequency))

    def update(self):
        if self.standby_pin is not None:
            self.standby_pin.value(not self._state == _STATE_SLEEP)
        self.i2c.writeto_mem(self.address, 0x10 | self.index,
            ustruct.pack(">BH", self._state, self._speed))

    def speed(self, speed=None):
        if speed is None:
            return self._speed
        if abs(speed) < 30 and abs(speed) > 10: # kick-start for small speeds
            self.speed(int(100 * sign(speed)))
            utime.sleep_ms(5)

        if speed > 0:
            self._speed = min(10000, max(1, speed))
            self._state = _STATE_RIGHT
        elif speed < 0:
            self._speed = min(10000, max(1, -speed))
            self._state = _STATE_LEFT
        else:
            self._speed = 0
            self._state = _STATE_STOP
        self.update()

    def sleep(self):
        self._speed = 0
        self._state = _STATE_SLEEP
        self.update()

    def brake(self):
        self._speed = 0
        self._state = _STATE_BRAKE
        self.update()

