/*
 * Updates only the wolf and squirrels that belong to a specific subgeneration.
 * color: the color of the subgeneration to be updated.
 */
void iterate_subgeneration(int color) {
    int i, j, start_col;
    struct world **read_matrix = worlds[0];
    struct world **write_matrix = worlds[1];

    for(i = 0; i < max_size; i++) {
        if(get_cell_color(i, 0) == color) {
            start_col = 0;
        }
        else {
            start_col = 1;
        }

        for(j = start_col; j < max_size; j += N_COLORS) {
            if(read_matrix[i][j].type & WOLF) {
                update_wolf(read_matrix, write_matrix, i,j);
            }
            else if(read_matrix[i][j].type & SQUIRREL) {
                update_squirrel(read_matrix, write_matrix, i,j);
            }
        }
    }
}
void start_world_simulation(void){
	register int i, j, btm_lim = bottom, top_lim = top;

	for(; number_of_generations > 0; --number_of_generations){
		copy_world();

		if(taskid != MASTER)
			btm_lim = bottom - 1;

		if(taskid != numtasks-1)
			top_lim = top + 1;

		/* update 'red' cells, think chessboard */
		#pragma omp parallel for private(j)
		for(i = btm_lim; i < top_lim; ++i){
			for (j = 0; j < grid_size; j++){
				if(get_cell_color(&world[i][j]) == RED){
					update_world_cell(i, j);
				}
			}
		}
	
		resolve_conflicts(RED, number_of_generations);
		copy_world();

		/* update 'black' cells, think chessboard */
		#pragma omp parallel for private(j)
		for(i = btm_lim; i < top_lim; ++i){
			for (j = 0; j < grid_size; j++){
				if(get_cell_color(&world[i][j]) == BLACK){
					update_world_cell(i, j);
				}
			}
		}

		resolve_conflicts(BLACK, number_of_generations);

		if(number_of_generations == 1)
			return;

		#pragma omp parallel for private(j)
		for(i = 0; i < payload; ++i){
			for (j = 0; j < grid_size; ++j){
				if (world[i][j].moved == UPDATED || world[i][j].moved == MOVED){
					if (world[i][j].type == SQUIRREL || world[i][j].type == SQUIRREL_IN_TREE){
						world[i][j].breeding_period++;
					} else if (world[i][j].type == WOLF){
						world[i][j].starvation_period--;
						world[i][j].breeding_period++;
						/* wolf dies of starvation */
						if(world[i][j].starvation_period <= 0){
							cleanup_cell(&world[i][j]);
						}
					}
				}
				world[i][j].moved = 0;
			}
		}	
	}
	
}
void resolve_conflicts(int generation_color, int gen_number){
	int i;
	world_cell* conf1 = malloc(grid_size*sizeof(world_cell));
	world_cell* conf2 = malloc(grid_size*sizeof(world_cell));

	//send to taskid-1
	if(taskid != MASTER){
		MPI_Request size_reqs[2];
		MPI_Isend(world[0], grid_size, mpi_world_cell_type, taskid-1, CONF_TAG, MPI_COMM_WORLD, &size_reqs[0]);
		MPI_Isend(world[1], grid_size, mpi_world_cell_type, taskid-1, CONF_TAG+1, MPI_COMM_WORLD, &size_reqs[1]);
	}
	
	//receive from taskid +1
	if(taskid != numtasks-1){
		MPI_Status status;
		MPI_Recv(conf1, grid_size, mpi_world_cell_type, taskid+1, CONF_TAG, MPI_COMM_WORLD, &status);
		MPI_Recv(conf2, grid_size, mpi_world_cell_type, taskid+1, CONF_TAG+1, MPI_COMM_WORLD, &status);
	}

	//resolve conflicts on n+1 n+2
	// Keep all moved cells from THIS generation (world), discard others
	for(i=0; i < grid_size; i++){
		//discard unmoved cells from my world with color 'generation_color'
		if(((world[payload-2][i].moved != UPDATED) && (world[payload-2][i].moved != NEW_BORN)) && get_cell_color(&world[payload-2][i]) == generation_color && world[payload-2][i].type != ICE && world[payload-2][i].type != TREE && world[payload-2][i].type != EMPTY){
			cleanup_cell(&world[payload-2][i]);
		} else if (world[payload-2][i].moved == UPDATED || world[payload-2][i].moved == NEW_BORN) {
			world[payload-2][i].moved = MOVED;
		}
		if(((world[payload-1][i].moved != UPDATED) && (world[payload-1][i].moved != NEW_BORN)) && get_cell_color(&world[payload-1][i]) == generation_color && world[payload-1][i].type != ICE && world[payload-1][i].type != TREE && world[payload-1][i].type != EMPTY){
			cleanup_cell(&world[payload-1][i]);
		} else if (world[payload-1][i].moved == UPDATED || world[payload-1][i].moved == NEW_BORN){
			world[payload-1][i].moved = MOVED;
		}
		if(conf1[i].moved == UPDATED || conf1[i].moved == NEW_BORN){
			//move to my world
			if(conf1[i].type == WOLF)
				move_wolf(&conf1[i], &world[payload-2][i]);
			else
				move_squirrel(&conf1[i], &world[payload-2][i]);

			world[payload-2][i].moved = MOVED;
		}
		if(conf2[i].moved == UPDATED || conf2[i].moved == NEW_BORN){
			//move to my world
			if(conf2[i].type == WOLF)
				move_wolf(&conf2[i], &world[payload-1][i]);
			else
				move_squirrel(&conf2[i], &world[payload-1][i]);

			world[payload-1][i].moved = MOVED;
		}
	}
	
	//send to taskid+1
	if(taskid != numtasks-1){
		MPI_Request size_reqs[2];
		MPI_Isend(world[payload-2], grid_size, mpi_world_cell_type, taskid+1, CONF_TAG-2, MPI_COMM_WORLD, &size_reqs[0]);
		MPI_Isend(world[payload-1], grid_size, mpi_world_cell_type, taskid+1, CONF_TAG-1, MPI_COMM_WORLD, &size_reqs[1]);
	}	
	//receive from taskid -1
	if(taskid != MASTER){
		MPI_Status status;
		MPI_Recv(world[0], grid_size, mpi_world_cell_type, taskid-1, CONF_TAG-2, MPI_COMM_WORLD, &status);
		MPI_Recv(world[1], grid_size, mpi_world_cell_type, taskid-1, CONF_TAG-1, MPI_COMM_WORLD, &status);
	}
}