#include "gui.h"
#include <pthread.h>
#include <unistd.h>
#include <time.h>
static SDL_Surface* screen;


//sets the entire screen to black
static void clear_objects(void);
static void draw_screen(void);
static void set_pixel(int x, int y, Uint8 r, Uint8 g, Uint8 b);

static int must_exit;

static void set_pixel(int x, int y, Uint8 r, Uint8 g, Uint8 b){
    Uint32 *pixmem32;
    Uint32 color;  
 
    color = SDL_MapRGB(screen->format, r, g, b);
  
    pixmem32 = (Uint32*)screen->pixels  + screen->w*y + x;
    *pixmem32 = color;
}


typedef struct {
    Uint8* r;
    Uint8* g;
    Uint8* b;
} screen_updates_t;

static int current_frame;//determines what thread has what
static int frame_type;//determines what type of frame we are displaying

static pthread_t gui_thread;
//double buffer everything, one reading, one writing
screen_updates_t** frames[2];

static pthread_mutex_t flip_lock;

static void* run_gui(void* args);
const int target_fps = 50;


static void* run_gui(void* args){

    clock_t frame_time;
    while (!must_exit){
        //get the time
        frame_time = clock();
        pthread_mutex_lock(&flip_lock);
        draw_screen();
        pthread_mutex_unlock(&flip_lock);
        frame_time = clock() - frame_time;//clock ticks passed
        frame_time /= CLOCKS_PER_SEC/1000000;//convert to useconds
        if (frame_time > (1000000/target_fps) && ((1000000/target_fps) - frame_time) > 0 ){
            usleep((useconds_t)(1000000/target_fps) - (useconds_t)frame_time);
        }
    }
    return NULL;
}

void init_gui(void){
    must_exit = 0;
    current_frame = 0;
    frame_type = 0;
    SDL_Init(SDL_INIT_VIDEO);
    
    screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT,
                              WINDOW_DEPTH, SDL_HWSURFACE | SDL_DOUBLEBUF );
    SDL_WM_SetCaption( WINDOW_TITLE, 0 );

    
    int i, j;
    for (i = 0; i < 2; i++){
        frames[i] = malloc(sizeof(screen_updates_t*)*4);
        for (j = 0; j < 4; j++){
            frames[i][j] = malloc(sizeof(screen_updates_t));
            frames[i][j]->r = malloc(sizeof(Uint8)*screen->w*screen->h);

            frames[i][j]->g = malloc(sizeof(Uint8)*screen->w*screen->h);
            frames[i][j]->b = malloc(sizeof(Uint8)*screen->w*screen->h);
        }
    }

    
    pthread_mutex_init(&flip_lock, NULL);

    pthread_create(&gui_thread, NULL, &run_gui, NULL);
    
}

int update_screen(void){
    //flip it, and then clear it
    pthread_mutex_lock(&flip_lock);
    current_frame = !current_frame;
    pthread_mutex_unlock(&flip_lock);

    clear_objects();

    return !must_exit;


}

void add_object(int x, int y, Uint8 r, Uint8 g, Uint8 b, int type){
    int loc = screen->w*y + x;

    //this sucks, pick something better, better yet, let SDL do it
    if (((int)frames[!current_frame][type]->r[loc] + (int)r) > 255){
        frames[!current_frame][type]->r[loc] = 255;
    } else {
        frames[!current_frame][type]->r[loc] += r;
    }
    if (((int)frames[!current_frame][type]->g[loc] + (int)g) > 255){
        frames[!current_frame][type]->g[loc] = 255;
    } else {
        frames[!current_frame][type]->g[loc] += g;
    }
    if (((int)frames[!current_frame][type]->b[loc] + (int)b) > 255){
        frames[!current_frame][type]->b[loc] = 255;
    } else {
        frames[!current_frame][type]->b[loc] += b;
    }

}

void destroy_gui(void){
    
    pthread_join(gui_thread, NULL);
    pthread_mutex_destroy(&flip_lock);

    int i, j;
    for (i = 0; i < 2; i++){
        for (j = 0; j < 4; j++){
            free(frames[i][j]->r);
            free(frames[i][j]->g);
            free(frames[i][j]->b);
            free(frames[i][j]);

        }
        free(frames[i]);
    }

    SDL_Quit();
    

}

//zeros all objects on the current frame being rendered
static void clear_objects(void){
    int i;
    for (i=0; i < 4; i++){
        memset(frames[!current_frame][i]->r, 0, screen->w*screen->h);
        memset(frames[!current_frame][i]->g, 0, screen->w*screen->h);
        memset(frames[!current_frame][i]->b, 0, screen->w*screen->h);
    }
}


static void draw_screen(void){

    if(SDL_MUSTLOCK(screen)){
        if(SDL_LockSurface(screen) < 0){
            return;
        }
    }
    int x, y;
    for(y = 0; y < screen->h; y++ ){
        for( x = 0; x < screen->w; x++ ){
            //there must be a better way
            set_pixel(x, y, frames[current_frame][frame_type]->r[screen->w*y + x],
                      frames[current_frame][frame_type]->g[screen->w*y + x],
                      frames[current_frame][frame_type]->b[screen->w*y + x]);
            
        }
    }
    /*
    static int frame  = 0;
    char* format = "screenshot/%.4d.bmp";
    char* name = malloc(sizeof(char)*(16 + strlen(format)));
    sprintf(name, format, frame);
    SDL_SaveBMP(screen, name);
    free(name);
    frame++;
    */
    if(SDL_MUSTLOCK(screen)){
        SDL_UnlockSurface(screen);
    }
  
    SDL_Flip(screen); 

    SDL_Event event;
    if (SDL_PollEvent(&event)){
        switch (event.type){
        case SDL_QUIT:
            must_exit = 1;
            break;
        case SDL_KEYDOWN:
            switch (event.key.keysym.sym){
            case SDLK_1:
                frame_type = 0;
                break;
            case SDLK_2:
                frame_type = 1;
                break;
            case SDLK_3:
                frame_type = 2;
                break;
            case SDLK_4:
                frame_type = 3;
                break;
            default:
                //scratch nuts
                break;
            }
            break;
             
        }
         
    }
}


