# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
`adafruit_tpa2016`
================================================================================

CircuitPython driver for TPA2016 Class D Amplifier.


* Author(s): Kattni Rembor

Implementation Notes
--------------------

**Hardware:**

* `Adafruit TPA2016 - I2C Control AGC <https://www.adafruit.com/product/1712>`_

**Software and Dependencies:**

* Adafruit CircuitPython firmware for the supported boards:
  https://github.com/adafruit/circuitpython/releases

* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
* Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
"""

from micropython import const
import adafruit_bus_device.i2c_device as i2cdevice
from adafruit_register.i2c_bits import RWBits
from adafruit_register.i2c_bit import RWBit

__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_TPA2016.git"


class TPA2016:
    """Driver for the TPA2016 class D amplifier.

    :param busio.I2C i2c_bus: The I2C bus the TPA2016 is connected to.

    """

    # Compression ratio settings
    COMPRESSION_1_1 = const(0x0)  # Ratio 1:1
    COMPRESSION_2_1 = const(0x1)  # Ratio 2:1
    COMPRESSION_4_1 = const(0x2)  # Ratio 4:1
    COMPRESSION_8_1 = const(0x3)  # Ratio 8:1

    # NoiseGate threshold settings
    NOISE_GATE_1 = const(0x0)  # 1mV
    NOISE_GATE_4 = const(0x1)  # 4mV
    NOISE_GATE_10 = const(0x2)  # 10mV
    NOISE_GATE_20 = const(0x3)  # 20mV

    _attack_control = RWBits(6, 0x02, 0)
    _release_control = RWBits(6, 0x03, 0)
    _hold_time_control = RWBits(6, 0x04, 0)
    _fixed_gain_control = RWBits(6, 0x05, 0)
    _output_limiter_level = RWBits(5, 0x05, 0)
    _max_gain = RWBits(4, 0x07, 4)

    speaker_enable_r = RWBit(0x01, 7)
    """Enables right speaker. Defaults to enabled. Set to ``False`` to disable."""
    speaker_enable_l = RWBit(0x01, 6)
    """Enables left speaker. Defaults to enabled. Set to ``False`` to disable."""
    amplifier_shutdown = RWBit(0x01, 5)
    """Amplifier shutdown. Amplifier is disabled if ``True``. Defaults to ``False``. If ``True``,
    device is in software shutdown, e.g. control, bias and oscillator are inactive."""
    reset_fault_r = RWBit(0x01, 4)
    """Over-current event on right channel indicated by returning ``True``. Reset by setting to
    ``False``."""
    reset_Fault_l = RWBit(0x01, 3)
    """Over-current event on left channel indicated by returning ``True``. Reset by setting to
    ``False``."""
    reset_thermal = RWBit(0x01, 2)
    """Thermal software shutdown indicated by returning ``True``. Reset by setting to ``False``."""

    noise_gate_enable = RWBit(0x01, 0)
    """NoiseGate function enable. Enabled by default. Can only be enabled when compression ratio
    is NOT 1:1. To disable, set to ``False``."""
    output_limiter_disable = RWBit(0x06, 7)
    """

    Output limiter disable.

    Enabled by default when compression ratio is NOT 1:1. Can only be
    disabled if compression ratio is 1:1. To disable, set to ``True``.
    """

    noise_gate_threshold = RWBits(2, 0x06, 5)
    """
    Noise Gate threshold in mV.

    Noise gate settings are 1mV, 4mV, 10mV, and 20mV. Settings
    options are NOISE_GATE_1, NOISE_GATE_4, NOISE_GATE_10, NOISE_GATE_20. Only functional when
    compression ratio is NOT 1:1. Defaults to 4mV.

    This example sets the noise gate threshold to 10mV.

    .. code-block:: python

        import adafruit_tpa2016
        import busio
        import board

        i2c = busio.I2C(board.SCL, board.SDA)
        tpa = adafruit_tpa2016.TPA2016(i2c)

        tpa.noise_gate_threshold = tpa.NOISE_GATE_10

    """

    compression_ratio = RWBits(2, 0x07, 0)
    """
    The compression ratio.

    Ratio settings are: 1:1. 2:1, 4:1, 8:1. Settings options are:
    COMPRESSION_1_1, COMPRESSION_2_1, COMPRESSION_4_1, COMPRESSION_8_1. Defaults to 4:1.

    This example sets the compression ratio to 2:1.

    .. code-block:: python

        import adafruit_tpa2016
        import busio
        import board

        i2c = busio.I2C(board.SCL, board.SDA)
        tpa = adafruit_tpa2016.TPA2016(i2c)

        tpa.compression_ratio = tpa.COMPRESSION_2_1

    """

    def __init__(self, i2c_bus):
        self.i2c_device = i2cdevice.I2CDevice(i2c_bus, 0x58)

    @property
    def attack_time(self):
        """The attack time. This is the minimum time between gain decreases. Set to ``1`` - ``63``
        where 1 = 0.1067ms and the time increases 0.1067ms with each step, for a maximum of 6.722ms.
        Defaults to 5, or 0.5335ms.

        This example sets the attack time to 1, or 0.1067ms.

        .. code-block:: python

            import adafruit_tpa2016
            import busio
            import board

            i2c = busio.I2C(board.SCL, board.SDA)
            tpa = adafruit_tpa2016.TPA2016(i2c)

            tpa.attack_time = 1

        """
        return self._attack_control

    @attack_time.setter
    def attack_time(self, value):
        if 1 <= value <= 63:
            self._attack_control = value
        else:
            raise ValueError("Attack time must be 1 to 63!")

    @property
    def release_time(self):
        """The release time. This is the minimum time between gain increases. Set to ``1`` - ``63``
        where 1 = 0.0137ms, and the time increases 0.0137ms with each step, for a maximum of
        0.8631ms. Defaults to 11, or 0.1507ms.

        This example sets release time to 1, or 0.0137ms.

        .. code-block:: python

            import adafruit_tpa2016
            import busio
            import board

            i2c = busio.I2C(board.SCL, board.SDA)
            tpa = adafruit_tpa2016.TPA2016(i2c)

            tpa.release_time = 1

        """
        return self._release_control

    @release_time.setter
    def release_time(self, value):
        if 1 <= value <= 63:
            self._release_control = value
        else:
            raise ValueError("Release time must be 1 to 63!")

    @property
    def hold_time(self):
        """The hold time. This is the minimum time between attack and release. Set to ``0`` -
        ``63`` where 0 = disabled, and the time increases 0.0137ms with each step, for a maximum of
        0.8631ms. Defaults to 0, or disabled.

        This example sets hold time to 1, or 0.0137ms.

        .. code-block:: python

            import adafruit_tpa2016
            import busio
            import board

            i2c = busio.I2C(board.SCL, board.SDA)
            tpa = adafruit_tpa2016.TPA2016(i2c)

            tpa.hold_time = 1

        """
        return self._hold_time_control

    @hold_time.setter
    def hold_time(self, value):
        if 0 <= value <= 63:
            self._hold_time_control = value
        else:
            raise ValueError("Hold time must be 0 to 63!")

    @property
    def fixed_gain(self):
        """The fixed gain of the amplifier in dB. If compression is enabled, fixed gain is
        adjustable from ``–28`` to ``30``. If compression is disabled, fixed gain is adjustable
        from ``0`` to ``30``.

        The following example sets the fixed gain to -16dB.

        .. code-block:: python

            import adafruit_tpa2016
            import busio
            import board

            i2c = busio.I2C(board.SCL, board.SDA)
            tpa = adafruit_tpa2016.TPA2016(i2c)

            tpa.fixed_gain = -16

        """
        return self._fixed_gain_control

    @fixed_gain.setter
    def fixed_gain(self, value):
        if self.compression_ratio:
            if -28 <= value <= 30:
                ratio = value & 0x3F
                self._fixed_gain_control = ratio
            else:
                raise ValueError("Gain must be -28 to 30!")
        else:
            if 0 <= value <= 30:
                self._fixed_gain_control = value
            else:
                raise ValueError("Compression is disabled, gain must be 0 to 30!")

    @property
    def output_limiter_level(self):
        """The output limiter level in dBV. Must be between ``-6.5`` and ``9``, set in increments
        of 0.5."""
        return -6.5 + 0.5 * self._output_limiter_level

    @output_limiter_level.setter
    def output_limiter_level(self, value):
        if -6.5 <= value <= 9:
            output = int((value + 6.5) / 0.5)
            self._output_limiter_level = output
        else:
            raise ValueError("Output limiter level must be -6.5 to 9!")

    @property
    def max_gain(self):
        """The max gain in dB. Must be between ``18`` and ``30``."""
        return self._max_gain + 18

    @max_gain.setter
    def max_gain(self, value):
        if 18 <= value <= 30:
            max_value = value - 18
            self._max_gain = max_value
        else:
            raise ValueError("Max gain must be 18 to 30!")
