
import ..UncertainDatasets: 
	AbstractUncertainValueDataset,
	UncertainValueDataset,
	UVAL_COLLECTION_TYPES

import ..SamplingConstraints:
	NoConstraint,
	TruncateLowerQuantile,
	TruncateUpperQuantile,
	TruncateQuantiles

"""
	resample(x::UVAL_COLLECTION_TYPES, constraint::SamplingConstraint) -> Vector{T} where T

Resample `x` (a collection of uncertain values) once, applying the provided sampling `constraint`.

Returns a `length(x)`-element vector. The `i`-th element of this vector is generated by 
truncating the `i`-th uncertain value by the sampling `constraint`, then drawing a single random 
number from the truncated value.

See also [`UVAL_COLLECTION_TYPES`](@ref).

## Example 

```julia
# Generate some uncertain values where the `i`-th value is given by a normal 
# distribution with mean `i` and a standard deviation drawn from a uniform 
# distribution on `[0, 1]`.
uvals = [UncertainValue(Normal(i, rand())) for i = 1:100]

# Truncate each distribution at +- 0.5 standard deviations, then resample. 
resample(uvals, TruncateStd(0.5))
```
"""
function resample(uv::UVAL_COLLECTION_TYPES, constraint::SamplingConstraint)
	[resample(uv.values[i], constraint) for i in 1:length(uv)]
end

"""
	resample(x::UVAL_COLLECTION_TYPES, constraint::Vector{<:SamplingConstraint}) -> Vector{T} where T

Resample `x` (a collection of uncertain values) once, applying the provided sampling `constraint`s.
The number of constraints must match the number of elements in `x`.

Returns a `length(x)`-element vector. The `i`-th element of this vector is generated by 
truncating the `i`-th uncertain value by the `i`-th sampling `constraint`, then drawing a single random 
number from the truncated value. 

See also [`UVAL_COLLECTION_TYPES`](@ref).

## Example

```julia
# Generate some uncertain values where the `i`-th value is given by a normal 
# distribution with mean `i` and a standard deviation drawn from a uniform 
# distribution on `[0, 1]`.
uvals = [UncertainValue(Normal(i, rand())) for i = 1:100]

# Truncate each distribution at +- 0.5 standard deviations, then resample. 
resample(uvals, TruncateStd(0.5))
```
"""
function resample(uv::UVAL_COLLECTION_TYPES, constraint::Vector{<:SamplingConstraint})
    [resample(uv.values[i], constraint[i]) for i in 1:length(uv)]
end

"""
	resample(x::UVAL_COLLECTION_TYPES, constraint::SamplingConstraint, n::Int) -> Vector{Vector{T}} where T

Resample `x` (a collection of uncertain values) `n` times, applying the provided sampling `constraint`.

Returns an `n`-element vector of `length(x)`-element vectors. Each of these vectors is an independent 
draw from `x`. The `i`-th element of each draw is generated by truncating the `i`-th uncertain value by 
the sampling `constraint`, then drawing a single random number from the truncated value.

See also [`UVAL_COLLECTION_TYPES`](@ref).

## Example 

```julia
# Generate some uncertain values where the `i`-th value is given by a normal 
# distribution with mean `i` and a standard deviation drawn from a uniform 
# distribution on `[0, 1]`.
uvals = [UncertainValue(Normal(i, rand())) for i = 1:100]

# Truncate the first 50 elements at the 90th percentile range, and the 
# last 50 elements at the 40th percentile range.
constraints = [i <= 50 ? TruncateQuantiles(0.05, 0.95) : TruncateQuantiles(0.3, 0.7) for i = 1:100]

# Truncate the distributions, then draw ten independent realisations of the collection subject
# to the provided constraints.
resample(uvals, constraints, 10)
```
"""
function resample(uv::UVAL_COLLECTION_TYPES, constraint::SamplingConstraint, n::Int)
	[[resample(uv.values[i], constraint) for i in 1:length(uv)] for k = 1:n]
end

"""
	resample(x::UVAL_COLLECTION_TYPES, constraint::Vector{<:SamplingConstraint}, n::Int) -> Vector{Vector{T}} where T

Resample `x` (a collection of uncertain values) `n` times, applying the provided sampling `constraint`s.

Returns an `n`-element vector of `length(x)`-element vectors. Each of these vectors is an independent 
draw from `x`. The `i`-th element of each draw is generated by truncating the `i`-th uncertain value by 
the `i`-th sampling `constraint`, then drawing a single random number from the truncated value.

See also [`UVAL_COLLECTION_TYPES`](@ref).

## Example 

```julia
# Generate some uncertain values where the `i`-th value is given by a normal 
# distribution with mean `i` and a standard deviation drawn from a uniform 
# distribution on `[0, 1]`.
uvals = [UncertainValue(Normal(i, rand())) for i = 1:100]

# Truncate the first 50 elements at `± 0.5` standard deviations, and the 
# last 50 elements at `± 1.2` standar deviations.
constraints = [i <= 50 ? TruncateStd(0.5) : TruncateStd(1.2) for i = 1:100]

# Apply the constraints element-wise, then draw ten independent realisations 
# of the collection subject to those constraints.
resample(uvals, constraints, 10)
```
"""
function resample(uv::UVAL_COLLECTION_TYPES, constraint::Vector{<:SamplingConstraint}, n::Int)
	[[resample(uv.values[i], constraint[i]) for i in 1:length(uv)] for k = 1:n]
end

"""
	resample(x::UVAL_COLLECTION_TYPES, constraint::SamplingConstraint, n::Int) -> Vector{Vector{T}} where T

Resample `x` (a collection of uncertain values) once by drawing a single random number from 
each of the uncertain values in `x`.

See also [`UVAL_COLLECTION_TYPES`](@ref).

## Example 

```julia 
# Generate some uncertain values represented by gamma distributions
uvals = [UncertainValue(Gamma(i, rand())) for i = 1:100]

# Resample the collection once 
resample(uvals)
```
"""
function resample(uvd::UVAL_COLLECTION_TYPES)
	L = length(uvd)
	[resample(uvd.values[i]) for i in 1:L]
end

"""
	resample(uvd::UVAL_COLLECTION_TYPES, n::Int) -> Vector{Vector{T}}

Draw `n` realisations of an uncertain value dataset according to the distributions
of the uncertain values comprising it.

See also [`UVAL_COLLECTION_TYPES`](@ref). 

## Example 

```julia 
# Generate some uncertain values represented by gamma distributions
uvals = [UncertainValue(Gamma(i, rand())) for i = 1:100]

# Resample the collection once 
resample(uvals)
```
"""
function resample(uvd::UVAL_COLLECTION_TYPES, n::Int)
	L = length(uvd)
	[[resample(uvd.values[i]) for i in 1:L] for k in 1:n]
end
