#ifndef CPPALLY_R_COPY_H
#define CPPALLY_R_COPY_H

#include <cppally/r_vec.h>
#include <cppally/r_attrs.h>

namespace cppally {

// Forward declarations

template <typename T>
inline T deep_copy(const T& x) = delete;
template<>
inline r_sexp deep_copy(const r_sexp& x);

template <typename T>
inline T shallow_copy(const T& x) = delete;
template<>
inline r_sexp shallow_copy(const r_sexp& x);

template <RVector T>
inline T deep_copy(const T& x){
    
    if (x.is_null()) return T(r_null);

    r_size_t n = x.length();
    T out(n);

    // If list, copy list elements
    if constexpr (is<T, r_vec<r_sexp>>){
        for (r_size_t i = 0; i < n; ++i){
            out.set(i, deep_copy(x.view(i)));
        }
    } else {
        r_copy_n(out, x, 0, n);
    }    
    if (attr::has_attrs(x)){
        r_vec<r_sexp> attrs = attr::get_attrs(x);
        int n_attrs = attrs.length();
        for (int i = 0; i < n_attrs; ++i){
            attrs.set(i, deep_copy(attrs.view(i)));
        }
        attr::set_attrs(out, attrs);
    }
    return out;
}

template<>
inline r_factors deep_copy(const r_factors& x){
    r_vec<r_int> out = deep_copy(x.value);
    return r_factors(unwrap(out), false);
}

template<>
inline r_df deep_copy(const r_df& x){
    return r_df(deep_copy(x.value));
}

// Symbols can't be deep copied
template<>
inline r_sym deep_copy(const r_sym& x){
    return x;
}


template <RVector T>
T shallow_copy(const T& x){
    if (x.is_null()) return x;
    r_size_t n = x.length();
    T out(n);
    for (r_size_t i = 0; i < n; ++i){
        out.set(i, x.view(i));
    }
    attr::set_attrs(out, attr::get_attrs(x));
    internal::share_name_cache(out, x);
    return out;
}

template<>
inline r_factors shallow_copy(const r_factors& x){
    r_vec<r_int> out = shallow_copy(x.value);
    r_factors result(unwrap(out), false);
    internal::share_levels_cache(result, x);
    return result;
}

template<>
inline r_df shallow_copy(const r_df& x){
    return r_df(shallow_copy(x.value), x.nrow(), internal::no_checks_tag{});
}

// Symbols can't be copied
template<>
inline r_sym shallow_copy(const r_sym& x){
    return x;
}


}
#endif
