#define ONCE alloc_if(1) free_if(1)
#define ALLOC alloc_if(1) free_if(0)
#define FREE alloc_if(0) free_if(1)
#define REUSE alloc_if(0) free_if(0)

const int mic_nr = 0;

#pragma offload_attribute (push, target(mic))

#include <offload.h>  // offload system header
#include <cstring> // for strcpy
#include <iostream>
#include <list>
#include <vector>

using namespace std;

// transfering vector
void fill_vector(vector<long>& v, long size, long* data)
{
  for (long i=0; i<size; i++)
    v[i] = data[i];
}

void fill_plain(long* data, long size, const vector<long>& v)
{
  for (long i=0; i<size; i++)
      data[i] = v[i];
}

// transfering list<vector>
// the vectors may have different lengths
// fill_list_vector also creates the list entries and appendes them to the list!
void fill_list_vector(list< vector<long> >& l, long plain_size, long* data)
{
  long* data_end = data + plain_size; // position after last entry
  while (data < data_end)
  {
    l.push_back(vector<long>(*data));
    fill_vector(l.back(), *data, data+1);
    data += *data + 1;
  }
}

void fill_plain(long* data, long size, const list< vector<long> >& l)
{
  long v_size;
  typename list< vector<long> >::const_iterator it;
  for (it = l.begin(); it != l.end(); it++)
  {
    v_size = it->size();
    *data = v_size;
    fill_plain(++data, v_size, *it);
    data += v_size;
  }
}

long plain_size(const list< vector<long> >& l)
{
  long size = 0;
  typename list< vector<long> >::const_iterator it;
  for (it = l.begin(); it != l.end(); it++)
    size += it->size() + 1;
  return size;
}

#pragma offload_attribute (pop)

//-------------------------- transfer methods ---------------------------------

void transfer_pyramids_inner(long *data, long size)
{
  #pragma offload target(mic:mic_nr) in(size) in(data: length(size) ONCE)
  {
    list< vector<long> > Pyramids;
    fill_list_vector(Pyramids, size, data);
    cout << "recieved " << Pyramids.size() << " pyramids." << endl;
  }
}

void transfer_pyramids(const list< vector<long> >& pyramids)
{
  long size = plain_size(pyramids);

  long *data = new long[size];
  fill_plain(data, size, pyramids);

  transfer_pyramids_inner(data, size);

  delete[] data;
  cout << "mic " << mic_nr << ": transfered " << pyramids.size() << " pyramids." << endl;
}

//---------------------------------------------------------------------------

void transfer_pyramids_old(const list< vector<long> >& pyramids)
{
    long size = plain_size(pyramids);

    long *data = new long[size];
    fill_plain(data, size, pyramids);

    #pragma offload target(mic:mic_nr) in(size) in(data: length(size) ONCE)
    {
        list< vector<long> > Pyramids;
        fill_list_vector(Pyramids, size, data);
        cout << "recieved " << Pyramids.size() << " pyramids." << endl;
    }
    delete[] data;
    cout << "mic " << mic_nr << ": transfered " << pyramids.size() << " pyramids." << endl;
}

//---------------------------------------------------------------------------

int main(int argc, char* argv[])
{
    list< vector<long> > pyramids;
    
    for (int i=0; i<8; i++) // initialize pyramids
    {
        pyramids.push_back(vector<long>((i%3)+4,i));
    }
    
    cout << "New transfer method:" << endl;
    transfer_pyramids(pyramids);

    cout << "Old transfer method:" << endl;
    transfer_pyramids_old(pyramids);
}

//---------------------------------------------------------------------------

