#include "memory-faucet.h"
#include "mf-utils.h"

#include "MemoryProcessing.h"
#ifdef INCLUDE_CUDA
#include "MemoryProcessing.cuh"
#endif

template< MemType mem, buf_choose_policy choose_type >
memory_pipe<mem, choose_type>::memory_pipe()
{
    buff_vec = std::vector<buffer > ();
}

template< MemType mem, buf_choose_policy choose_type >
memory_pipe<mem, choose_type>::~memory_pipe()
{
    buff_vec.clear();
}

template< MemType mem, buf_choose_policy choose_type >
void memory_pipe<mem, choose_type>::set_available(const int id)
{
    buff_vec[id].set_status(true);
}

template< MemType mem, buf_choose_policy choose_type >
std::vector<buffer >& memory_pipe<mem, choose_type>::get_buff_vec()
{
    return buff_vec;
}

template class memory_pipe<MemType::CPU, buf_choose_policy::naive>;
template class memory_pipe<MemType::CPU, buf_choose_policy::sorted_vec>;
template class memory_pipe<MemType::CPU, buf_choose_policy::find_best_unsorted>;

#ifdef INCLUDE_CUDA
template class memory_pipe<MemType::GPU, buf_choose_policy::naive>;
template class memory_pipe<MemType::GPU, buf_choose_policy::sorted_vec>;
template class memory_pipe<MemType::GPU, buf_choose_policy::find_best_unsorted>;
#endif


template< MemType mem, buf_choose_policy choose_type >
memory_pipe<mem, choose_type>& memory_faucet::get_faucet()
{
    static memory_pipe<mem, choose_type> mem_pipe = memory_pipe<mem, choose_type>();
    return mem_pipe;
}

template memory_pipe<MemType::CPU, buf_choose_policy::naive>& memory_faucet::get_faucet();
template memory_pipe<MemType::CPU, buf_choose_policy::sorted_vec>& memory_faucet::get_faucet();
template memory_pipe<MemType::CPU, buf_choose_policy::find_best_unsorted>& memory_faucet::get_faucet();

#ifdef INCLUDE_CUDA
template memory_pipe<MemType::GPU, buf_choose_policy::naive>& memory_faucet::get_faucet();
template memory_pipe<MemType::GPU, buf_choose_policy::sorted_vec>& memory_faucet::get_faucet();
template memory_pipe<MemType::GPU, buf_choose_policy::find_best_unsorted>& memory_faucet::get_faucet();
#endif

template< MemType mem, buf_choose_policy choose_type >
memBuf<mem, choose_type>::memBuf(const size_t required_size)
{
    memory_pipe<mem, choose_type>& mem_pipe = memory_faucet::get_faucet<mem, choose_type>();
    id = mf_utils::get_buffer<choose_type>(required_size, mem_pipe.get_buff_vec(), mem, &buf);
    size = required_size;
}

template< MemType mem, buf_choose_policy choose_type >
void memBuf<mem, choose_type>::free_memory()
{
    if(id == -1)
    {
        return;
    }

    memory_pipe<mem, choose_type>& mem_pipe = memory_faucet::get_faucet<mem, choose_type>();
    mem_pipe.set_available(id);
    buf = nullptr;
}

template< MemType mem, buf_choose_policy choose_type >
memBuf<mem, choose_type>::~memBuf()
{
    free_memory();
}

template< MemType mem, buf_choose_policy choose_type >
void* memBuf<mem, choose_type>::ptr()
{
    return buf;
}

template< MemType mem, buf_choose_policy choose_type >
void** memBuf<mem, choose_type>::ptr()
{
    return &buf;
}

template< MemType mem, buf_choose_policy choose_type >
const void* memBuf<mem, choose_type>::ptr() const
{
    return buf;
}

template< MemType mem, buf_choose_policy choose_type >
int memBuf<mem, choose_type>::get_size() const
{
    return size;
}

template< MemType mem, buf_choose_policy choose_type >
int memBuf<mem, choose_type>::get_id() const
{
    return id;
}

template< MemType mem, buf_choose_policy choose_type >
memBuf<mem, choose_type>::memBuf() 
{
    buf = nullptr;
    id = -1;
    size = 0;
}

template< MemType mem, buf_choose_policy choose_type >
void memBuf<mem, choose_type>::get_memory(const size_t required_size) 
{
    free_memory();

    memory_pipe<mem, choose_type>& mem_pipe = memory_faucet::get_faucet<mem, choose_type>();
    id = mf_utils::get_buffer<choose_type>(required_size, mem_pipe.get_buff_vec(), mem, &buf);
    size = required_size;
}

template class memBuf<MemType::CPU, buf_choose_policy::naive>;
template class memBuf<MemType::CPU, buf_choose_policy::sorted_vec>;
template class memBuf<MemType::CPU, buf_choose_policy::find_best_unsorted>;

#ifdef INCLUDE_CUDA
template class memBuf<MemType::GPU, buf_choose_policy::naive>;
template class memBuf<MemType::GPU, buf_choose_policy::sorted_vec>;
template class memBuf<MemType::GPU, buf_choose_policy::find_best_unsorted>;
#endif