# Please feel free to contribute other compartment models to the current list.

"""
    unpack(p::AbstractMatrix, t::Real)

Enables the use of parameters that change over time. Collects the column of the 
parameter matrix that matches the current intergrator time point.
"""
function unpack(p::AbstractMatrix, t::Real)
    view = @view p[1, :]
    index = findlast(<(t), view)
    return @view p[2:end, index === nothing ? 1 : index] # Should be a view
    # Above saves one allocation versus below ðŸŒ 
    # index = findlast(<(t), p[1, :])
    # return p[2:end, index === nothing ? 1 : index] # Should be a view
end

"""
    unpack(p::AbstractVector, t::Real)

For Vector-based `p` this function just returns `p`.
"""
unpack(p::AbstractVector, ::Real) = p


"""
    one_comp!(dA, A, p, t)

In-place function for the one-compartment model:
`dA/dt = (I / Vd) - Aâ‚� â‹… kâ‚�â‚€`

# Model parameters
- `CL`: Drug clearance estimate.
- `Vd`: Drug volume of distribution estimate.
- `I`: Rate of drug infusion; handled by callback function.
# Inferred parameters
- `kâ‚�â‚€`: CL / Vd
"""
function one_comp!(dA, A, p, t)
    CL, Vd, I = unpack(p, t)
    kâ‚�â‚€ = CL / Vd

    dA[1] = (I / Vd) - A[1] * kâ‚�â‚€
end

"""
    one_comp_abs!(dA, A, p, t)

In-place function for the one-compartment model with an absorption compartment:
```
dAâ‚�/dt = I - kâ‚� â‹… Aâ‚�
dAâ‚‚/dt = kâ‚� â‹… Aâ‚� - Aâ‚‚ â‹… kâ‚�â‚€
```

# Model parameters
- `kâ‚�`: Rate of drug absorption.
- `kâ‚�â‚€`: Rate of drug elimination.
- `I`: Rate of drug infusion; handled by callback function.
"""
function one_comp_abs!(dA, A, p, t)
    kâ‚�, kâ‚�â‚€, I = unpack(p, t)
    
    dA[1] = I - kâ‚� * A[1]
    dA[2] = kâ‚� * A[1] - A[2] * kâ‚�â‚€
end

"""
    two_comp!(dA, A, p, t)

In-place function for the two-compartment model:
```
dAâ‚�/dt = (I / Vâ‚�) + Aâ‚‚ â‹… kâ‚‚â‚� - Aâ‚� â‹… (kâ‚�â‚€ + kâ‚�â‚‚)
dAâ‚‚/dt = Aâ‚� â‹… kâ‚�â‚‚ - Aâ‚‚ â‹… kâ‚‚â‚�
```

# Model Parameters
- `CL`: Drug clearance from the first compartment.
- `Vâ‚�`: Central volume of distribution (in Aâ‚�).
- `Q`: Inter-compartmental clearance.
- `Vâ‚‚`: Peripheral volume of distribution (in Aâ‚‚).
- `I`: Rate of drug infusion; handled by callback function.
# Inferred parameters
- `kâ‚�â‚€`: CL / Vâ‚�
- `kâ‚�â‚‚`: Q / Vâ‚�
- `kâ‚‚â‚�`: Q / Vâ‚‚
"""
function two_comp!(dA, A, p, t)
    CL, V1, Q, V2, I = unpack(p, t)
    
    kâ‚�â‚€ = CL / V1
    kâ‚�â‚‚ = Q / V1
    kâ‚‚â‚� = Q / V2

    dA[1] = (I / V1) + A[2] * kâ‚‚â‚� - A[1] * (kâ‚�â‚€ + kâ‚�â‚‚)
    dA[2] = A[1] * kâ‚�â‚‚ - A[2] * kâ‚‚â‚�    
end

"""
    two_comp_abs!(dA, A, p, t)

In-place function for the two-compartment model with an absorption compartment:
```
dAâ‚�/dt = I - kâ‚� â‹… Aâ‚�
dAâ‚‚/dt = kâ‚� â‹… Aâ‚� + Aâ‚ƒ â‹… kâ‚ƒâ‚‚ - Aâ‚‚ â‹… (kâ‚‚â‚€ + kâ‚‚â‚ƒ)
dAâ‚ƒ/dt = Aâ‚‚ â‹… kâ‚‚â‚ƒ - Aâ‚ƒ â‹… kâ‚ƒâ‚‚
```

# Model parameters
- `kâ‚�`: Rate of drug absorption.
- `kâ‚‚â‚€`: Rate of drug elimination.
- `kâ‚‚â‚ƒ`: Rate of drug moving from compartment 2 to 3.
- `kâ‚ƒâ‚‚`: Rate of drug moving from compartment 3 to 2.
- `I`: Rate of drug infusion; handled by callback function.
"""
function two_comp_abs!(dA, A, p, t)
    kâ‚�, kâ‚‚â‚€, kâ‚‚â‚ƒ, kâ‚ƒâ‚‚, I = unpack(p, t)

    dA[1] = I - kâ‚� * A[1]
    dA[2] = kâ‚� * A[1] + A[3] * kâ‚ƒâ‚‚ - A[2] * (kâ‚‚â‚€ + kâ‚‚â‚ƒ)
    dA[3] = A[2] * kâ‚‚â‚ƒ - A[3] * kâ‚ƒâ‚‚
end
