import StochasticIntegrals.to_dataframe

"""
    to_dataframe(covar::CovarianceMatrix, othercols::Dict = Dict{Symbol,Any}();
                 delete_duplicate_correlations::Bool = true)

Convert a CovarianceMatrix to a `DataFrame` format.
### Inputs
* `covar` - The `CovarianceMatrix`
* `othercols` - This adds columns to the `DataFrame`. For instance if it is `Dict{Symbol,String}([:pc] .=> ["Fred's PC"])`, then there will be a column indicating that this estimation was done on Fred's PC.
* `delete_duplicate_correlations` - Should the unnecessary correlations be included (as correlation matrices are symmetric half the entries duplicate information).
### Returns
* A `DataFrame`.
"""
function to_dataframe(covar::CovarianceMatrix, othercols::Dict = Dict{Symbol,Any}(); delete_duplicate_correlations::Bool = true)
    d = size(covar.correlation)[1]
    corrs = DataFrame(asset1    = vcat(map(a -> repeat([a], d), covar.labels  )...),asset2 = Array{Union{Symbol,Missing}}(repeat(covar.labels, d)),value = vec(covar.correlation))
    corrs[!,:variable]          = repeat([:correlation], nrow(corrs))
    corrs[!,:vol_period_units]  = Array{Union{String,Missing}}(repeat([missing], nrow(corrs)))
    corrs[!,:vol_period]        = Array{Union{Integer,Missing}}(repeat([missing], nrow(corrs)))
    if delete_duplicate_correlations corrs = corrs[findall(map(a -> findfirst(covar.labels .== a), corrs[:,:asset1]) .< map(a -> findfirst(covar.labels .== a), corrs[:,:asset2])),:] end
    vols                        = DataFrame(asset1 = covar.labels, asset2 = covar.labels, value = covar.volatility)
    vols[!,:variable]           = repeat([:volatility], nrow(vols))
    vols[!,:vol_period_units]   = Array{Union{String,Missing}}(repeat([string(typeof(covar.time_period_per_unit))], nrow(vols)))
    vols[!,:vol_period]         = Array{Union{Integer,Missing}}(repeat([covar.time_period_per_unit.value], nrow(vols)))

    result = append!(corrs, vols)
    for k in keys(othercols)
        result[!,k] = repeat([othercols[k]], nrow(result))
    end
    return result
end


"""
    dataframe_to_covariancematrix(dd::DataFrame)

Convert a `CovarianceMatrix` to a `DataFrame` format.
### Inputs
* `dd` - A `DataFrame` of the form generated by the `to_dataframe` function.
### Returns
* A `CovarianceMatrix` struct.
"""
function dataframe_to_covariancematrix(dd::DataFrame)
    vol_dd = dd[dd[:,:variable] .== :volatility,:]
    assets = vol_dd[:,:asset1]
    vols   = vol_dd[:,:value]
    assets = vol_dd[:,:asset1]

    cor_dd = dd[dd[:,:variable] .== :correlation,:]
    N = length(assets)
    mat = zeros(N,N)
    for i in 1:N
        asseti = assets[i]
        for j in i:N
            assetj = assets[j]
            if (i == j)
                mat[i,j] = 1
            else
                c1 = cor_dd[cor_dd[:,:asset1] .== asseti,:]
                c1 = c1[c1[:,:asset2] .== assetj,:]
                if nrow(c1) > 0
                    mat[i,j] = c1[1,:value]
                else
                    c2 = cor_dd[cor_dd[:,:asset2] .== asseti,:]
                    c2 = c2[c2[:,:asset1] .== assetj,:]
                    if nrow(c2) == 0 error("The correlation between ", asseti, " and ", assetj, " is not in this dataframe.") end
                    mat[i,j] = c2[1,:value]
                end
            end
        end
    end

    # Vol Period
    volperiod = map( i ->  eval(Meta.parse(string(vol_dd[i,:vol_period_units] , "(", vol_dd[i,:vol_period], ")"))), 1:nrow(vol_dd))
    chosen_volperiod = volperiod[1]
    vols2 = convert_vol.(vols, volperiod, Ref(chosen_volperiod))
    return CovarianceMatrix(Hermitian(mat), vols2, assets, chosen_volperiod)
end
