package org.openrndr.extra.envelopes

import org.openrndr.math.mix
import kotlin.math.min

data class ADSR(
    val attackDuration: Double,
    val decayDuration: Double,
    val sustainValue: Double,
    val releaseDuration: Double
) : Envelope() {
    override fun value(t: Double, tOff: Double): Double {
        return adsr(attackDuration, decayDuration, sustainValue, releaseDuration, t, tOff)
    }

    override fun position(t: Double, tOff: Double): Double {
        return adsrPosition(attackDuration, decayDuration, releaseDuration, t, tOff)
    }

    override fun isActive(t: Double, tOff: Double): Boolean {
        return !(t - tOff > releaseDuration)
    }
}

fun adsr(
    attackDuration: Double,
    decayDuration: Double,
    sustainValue: Double,
    releaseDuration: Double,
    t: Double,
    tOff: Double = 1E10
): Double {
    val da = t / attackDuration
    val dc = (t - attackDuration) / decayDuration
    val vOn = mix(min(1.0, da), sustainValue, dc.coerceIn(0.0..1.0))
    return mix(vOn, 0.0, ((t - tOff) / releaseDuration).coerceIn(0.0..1.0))
}

fun adsrPosition(
    attackDuration: Double,
    decayDuration: Double,
    releaseDuration: Double,
    t: Double,
    tOff: Double
): Double {
    val ta = (t / attackDuration).coerceIn(0.0..1.0)
    val td = ((t - attackDuration) / decayDuration).coerceIn(0.0..1.0)
    val tr = ((t - tOff) / releaseDuration).coerceIn(0.0..1.0)
    return (ta + td + tr) / 3.0
}