/**
 * pre-OpenCL 1.1 compatibility macros
*/

#if __OPENCL_VERSION__ <= CL_VERSION_1_0
    #define atomic_add(p, v) atom_add(p, v)
    #define atomic_inc(p) atom_inc(p)
#endif

typedef struct {
	int width;
	int height;
	
	float growthRate;
	float diffusionRate;
	float diffusionThreshold;
	float mortalityRate;
	
	int speciality;
} CollemWorld;

#define PREF_FOREST    1
#define PREF_CULTURAL  2
#define PREF_CITY      3

#define MATCH_PREFERENCE(pref, type) \
    ((pref == PREF_FOREST  && (type == 6 || type == 7 || type == 8 || type == 10)) \
 || ((pref == PREF_CULTURE && (type == 3 || type == 4 || type == 5)) \
 || ((pref == PREF_CITY    && (type == 1)

/*
kernel void collembola(global int * patches, int width)
{
	const int i = get_global_id(0);
    const int j = get_global_id(1);
    
    //patches[i * width + j] = -patches[i * width + j];
    
    // Diffusion
    
}*/

#define CELL(world, values, x, y) (values[x * world->width + y])
#define OVERFLOW(world, values, x, y)(values[(x + 1) * (world->width + 2) + 1 + y])

kernel void newArrivals(
	global CollemWorld *world,
	global int *populations,
	global int *patchesOwners,
	global int *plotsPopulations,
	global int *plotsSurfaces)
{
	const int i = get_global_id(0);
    const int j = get_global_id(1);
    
    const int plotId = CELL(world, patchesOwners, i, j);
    if (plotId == -1) return;
    
    CELL(world, populations, i, j) =
    	CELL(world, populations, i, j) + (world->growthRate * plotsPopulations[plotId]) / plotsSurfaces[plotId];
}

kernel void reproduction(
	global CollemWorld *world,
	global int *populations,
	global int *patchesOwners,
	global int *plotsPopulations)
{
	const int i = get_global_id(0);
    const int j = get_global_id(1);
    
    const int plotId = CELL(world, patchesOwners, i, j);
    if (plotId == -1) return;
    
    atomic_add(plotsPopulations + plotId, CELL(world, populations, i, j));
}

kernel void diffusion(
	global CollemWorld *world,
	global int *populations,
	global int *overflows,
	global int *newPopulations)
{
	const int i = get_global_id(0);
    const int j = get_global_id(1);
    
	// Determine if we have something to diffuse
	
	/*if (CELL(world, populations, i, j) > world->diffusionThreshold) {
		OVERFLOW(world, overflows, i, j) = CELL(world, populations, i, j) * world->diffusionRate;
		//OVERFLOW(world, overflows, i, j) = 0;
	}*/
	OVERFLOW(world, overflows, i, j) = CELL(world, populations, i, j) * world->diffusionRate;
	
	barrier(CLK_GLOBAL_MEM_FENCE);
	
	int surplus = OVERFLOW(world, overflows, i + 1, j) + OVERFLOW(world, overflows, i - 1, j) + OVERFLOW(world, overflows, i, j - 1) + OVERFLOW(world, overflows, i, j + 1);
	//int surplus = 0;
	
	CELL(world, newPopulations, i, j) = CELL(world, populations, i, j) + surplus / 8.0 - OVERFLOW(world, overflows, i, j);
	
	barrier(CLK_GLOBAL_MEM_FENCE);
	//CELL(world, newPopulations, i, j) = world->width * 1000 + world->height;
	//CELL(world, newPopulations, i, j) = surplus;
}

kernel void death(
	global CollemWorld *world,
	global int *populations,
	global int *patchesType)
{
	/*const int i = get_global_id(0);
    const int j = get_global_id(1);
    
    int patchType = CELL(world, patchesType, i, j);
    
    if (patchType == TYPE_ARTIFICIAL) {
    	CELL(world, populations, i, j) = 0;
    } else if (patchType != world->speciality) {
    	CELL(world, populations, i, j) -= CELL(world, populations, i, j) * world->mortalityRate;
    }*/
}

