import ctypes

from . import ecodes

_u8 = ctypes.c_uint8
_u16 = ctypes.c_uint16
_u32 = ctypes.c_uint32
_s16 = ctypes.c_int16
_s32 = ctypes.c_int32


class Replay(ctypes.Structure):
    """
    Defines scheduling of the force-feedback effect
    @length: duration of the effect
    @delay: delay before effect should start playing
    """

    _fields_ = [
        ("length", _u16),
        ("delay", _u16),
    ]


class Trigger(ctypes.Structure):
    """
    Defines what triggers the force-feedback effect
    @button: number of the button triggering the effect
    @interval: controls how soon the effect can be re-triggered
    """

    _fields_ = [
        ("button", _u16),
        ("interval", _u16),
    ]


class Envelope(ctypes.Structure):
    """
    Generic force-feedback effect envelope
    @attack_length: duration of the attack (ms)
    @attack_level: level at the beginning of the attack
    @fade_length: duration of fade (ms)
    @fade_level: level at the end of fade

    The @attack_level and @fade_level are absolute values; when applying
    envelope force-feedback core will convert to positive/negative
    value based on polarity of the default level of the effect.
    Valid range for the attack and fade levels is 0x0000 - 0x7fff
    """

    _fields_ = [
        ("attack_length", _u16),
        ("attack_level", _u16),
        ("fade_length", _u16),
        ("fade_level", _u16),
    ]


class Constant(ctypes.Structure):
    """
    Defines parameters of a constant force-feedback effect
    @level: strength of the effect; may be negative
    @envelope: envelope data
    """

    _fields_ = [
        ("level", _s16),
        ("ff_envelope", Envelope),
    ]


class Ramp(ctypes.Structure):
    """
    Defines parameters of a ramp force-feedback effect
    @start_level: beginning strength of the effect; may be negative
    @end_level: final strength of the effect; may be negative
    @envelope: envelope data
    """

    _fields_ = [
        ("start_level", _s16),
        ("end_level", _s16),
        ("ff_envelope", Envelope),
    ]


class Condition(ctypes.Structure):
    """
    Defines a spring or friction force-feedback effect
    @right_saturation: maximum level when joystick moved all way to the right
    @left_saturation: same for the left side
    @right_coeff: controls how fast the force grows when the joystick moves to the right
    @left_coeff: same for the left side
    @deadband: size of the dead zone, where no force is produced
    @center: position of the dead zone
    """

    _fields_ = [
        ("right_saturation", _u16),
        ("left_saturation", _u16),
        ("right_coeff", _s16),
        ("left_coeff", _s16),
        ("deadband", _u16),
        ("center", _s16),
    ]


class Periodic(ctypes.Structure):
    """
    Defines parameters of a periodic force-feedback effect
    @waveform: kind of the effect (wave)
    @period: period of the wave (ms)
    @magnitude: peak value
    @offset: mean value of the wave (roughly)
    @phase: 'horizontal' shift
    @envelope: envelope data
    @custom_len: number of samples (FF_CUSTOM only)
    @custom_data: buffer of samples (FF_CUSTOM only)
    """

    _fields_ = [
        ("waveform", _u16),
        ("period", _u16),
        ("magnitude", _s16),
        ("offset", _s16),
        ("phase", _u16),
        ("envelope", Envelope),
        ("custom_len", _u32),
        ("custom_data", ctypes.POINTER(_s16)),
    ]


class Rumble(ctypes.Structure):
    """
    Defines parameters of a periodic force-feedback effect
    @strong_magnitude: magnitude of the heavy motor
    @weak_magnitude: magnitude of the light one

    Some rumble pads have two motors of different weight. Strong_magnitude
    represents the magnitude of the vibration generated by the heavy one.
    """

    _fields_ = [
        ("strong_magnitude", _u16),
        ("weak_magnitude", _u16),
    ]


class EffectType(ctypes.Union):
    _fields_ = [
        ("ff_constant_effect", Constant),
        ("ff_ramp_effect", Ramp),
        ("ff_periodic_effect", Periodic),
        ("ff_condition_effect", Condition * 2),  # one for each axis
        ("ff_rumble_effect", Rumble),
    ]


class Effect(ctypes.Structure):
    _fields_ = [
        ("type", _u16),
        ("id", _s16),
        ("direction", _u16),
        ("ff_trigger", Trigger),
        ("ff_replay", Replay),
        ("u", EffectType),
    ]


class UInputUpload(ctypes.Structure):
    _fields_ = [
        ("request_id", _u32),
        ("retval", _s32),
        ("effect", Effect),
        ("old", Effect),
    ]


class UInputErase(ctypes.Structure):
    _fields_ = [
        ("request_id", _u32),
        ("retval", _s32),
        ("effect_id", _u32),
    ]


# ff_types = {
#     ecodes.FF_CONSTANT,
#     ecodes.FF_PERIODIC,
#     ecodes.FF_RAMP,
#     ecodes.FF_SPRING,
#     ecodes.FF_FRICTION,
#     ecodes.FF_DAMPER,
#     ecodes.FF_RUMBLE,
#     ecodes.FF_INERTIA,
#     ecodes.FF_CUSTOM,
# }
