#include "world.h"
#include "gui.h"
#include "elements.h"
#include "life.h"

static struct {
    world_cell_t* world_cells;
    int x;
    int y;
} world;


static void add_cell(int x, int y);


typedef struct {
    int y0;
    int y1;
} row_coords_t;
static void* thread_add_points(void* data);

static pthread_barrier_t point_start, point_end;
static pthread_t* point_workers;
void init_world(int x, int y){
    world.world_cells = malloc(sizeof(world_cell_t)*x*y);
    world.x = x;
    world.y = y;
    world_cell_t* cell;
    int i;
    for (y = 0; y < world.y; y++){
        for (x = 0; x < world.x; x++){

            cell = get_cell(x, y);
            
            //fill the world with stuff
            cell->light = (x*256)/world.x;
            cell->water = (y*256)/world.y;
            cell->x = x;
            cell->y = y;
            cell->lock = NULL;
            init_elements(&cell->elements, 1024);
            init_life(cell);
        }
    }
    //make the threads
    pthread_barrier_init(&point_start, NULL, WORKER_THREADS + 1);
    pthread_barrier_init(&point_end, NULL, WORKER_THREADS + 1);
    point_workers = malloc(sizeof(pthread_t)*WORKER_THREADS);
    for (i = 0; i < WORKER_THREADS; i++){
        row_coords_t* r = malloc(sizeof(row_coords_t));
        r->y0 = (i*WINDOW_HEIGHT)/WORKER_THREADS;
        r->y1 = ((i+1)*WINDOW_HEIGHT)/WORKER_THREADS;
        pthread_create(&point_workers[i], NULL, thread_add_points, (void*)r);
    }
}

world_cell_t* get_cell(int x, int y){
    return &world.world_cells[x*world.y+y];
}

void destroy_world(void){
    int x, y;
    world_cell_t* cell;
    for (x = 0; x < world.x; x++){
        for (y = 0; y < world.y; y++){
            cell = get_cell(x, y);
            destroy_elements(&cell->elements);
        }
    }    
    free(world.world_cells);
}

void execute_world(void){
    //run the life simulation
    wait_for_compete();
    
}

int draw_world(void){
    //draw the life simulation
    pthread_barrier_wait(&point_start);
    //and we wait for the threads to work...
    pthread_barrier_wait(&point_end);
    return 0;
}


static void add_cell(int x, int y){
    life_list_t* search_state;
    life_list_t* life_instance;
    world_cell_t* cell;
    cell = get_cell(x, y);
   
    //draw the water
    //add_object(x, y,  0, 0, (Uint8)(cell->water%256));//water shows as blue
    //add_object(x, y,  (Uint8)((cell->light)%256), (Uint8)((cell->light)%256), 0);//light shows as yellow
   
    //go through all life forms and print their colors
    search_state = NULL;
    while ((life_instance = get_next_instance(&cell->life, &search_state)) != NULL){
        assert(life_instance->parent_list->head != NULL);
        assert(life_instance->parent_list->tail != NULL);
        add_object(x, y, get_color_r(life_instance), get_color_g(life_instance), get_color_b(life_instance), 0);
       
        switch (get_life_value_mod(life_instance, 9, 4)){
        case 0:
            //light
            add_object(x, y, 255, 0, 0, 1);
            break;
        case 1:
            add_object(x, y, 0, 255, 0, 2);
            break;
        case 2:
            add_object(x, y, 0, 0, 255, 2);
            break;
        case 3:
        default:
            add_object(x, y, 255, 255, 255, 3);
            break;
        }
       
    }
}


static void* thread_add_points(void* data){
    row_coords_t* p = (row_coords_t*)data;

    int x, y;
    while (1){
        pthread_barrier_wait(&point_start);
        for (y = p->y0; y < p->y1; y++){
            for (x = 0; x < world.x; x++){
                add_cell(x, y);
            }
        }
        pthread_barrier_wait(&point_end);
    }
    free(data);
    return NULL;
}
