/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

#include "graphique.h"

////////////////////////////////////////////////////////////////////////////////
/**
 * <b>initWindow</b> initialise la SDL, crée une fenêtre et initialise un 
 * Timer pour scruter les évènements.
 * @author SD
 * @param <b>title</b> : Le titre de la fenêtre
 * @param <b>width</b> : La largeur de la fenêtre en pixels
 * @param <b>height</b> : La hauteur de la fenêtre en pixels
 * @return 0 en cas de succès, -1 sinon
*/
int initWindow(const char* title, unsigned width, unsigned height) {
    /* Initialisation de la DSL */
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0 )
    {
        fprintf(stdout,"Échec de l'initialisation de la SDL (%s)\n",SDL_GetError());
        return -1;
    }

    /* Création de la fenêtre */
    __graphique.pWindow = SDL_CreateWindow(title,
                                SDL_WINDOWPOS_UNDEFINED,
                                SDL_WINDOWPOS_UNDEFINED,
                                width,
                                height,
                                SDL_WINDOW_SHOWN);

    if(__graphique.pWindow) {        
        __graphique.screen = SDL_GetWindowSurface(__graphique.pWindow);            
        
        __graphique.fill_red = 0; 
        __graphique.fill_green = 0;
        __graphique.fill_blue = 0;
        
        // Timer pour appeler la fonction _poolEvent toutes les 25ms
        Uint32 delay = 25;
        SDL_TimerID my_timer_id = SDL_AddTimer(delay, _poolEvent, 0);
    }
    else {
        fprintf(stderr,"Erreur de création de la fenêtre: %s\n",SDL_GetError());
		return -1;
    }
    
    return 0;
}


////////////////////////////////////////////////////////////////////////////////
/**
 * <b>exitWindow</b> Ferme la fenêtre créé avec initWindow().
 * @author SD
*/
void exitWindow() {
    SDL_DestroyWindow(__graphique.pWindow);
    SDL_Quit();
}


////////////////////////////////////////////////////////////////////////////////
/**
 * <b>backgroundWhite</b> Efface la fenêtre avec un fond blanc.
 * @author SD
*/
void backgroundWhite() {
    SDL_FillRect(__graphique.screen, 
            NULL, 
            SDL_MapRGB(__graphique.screen->format, 255, 255, 255));
}


////////////////////////////////////////////////////////////////////////////////
/**
 * <b>background</b> Efface la fenêtre avec un fond selon les composantes RVB
 *  passées en paramètres.
 * @author SD
 * @param <b>red</b> : Composante rouge (0 - 255)
 * @param <b>green</b> : Composante verte (0 - 255)
 * @param <b>blue</b> : Composante bleue (0 - 255)
*/
void background(Uint8 red, Uint8 green, Uint8 blue) {
    SDL_FillRect(__graphique.screen, 
            NULL, 
            SDL_MapRGB(__graphique.screen->format, red, green, blue));
}


////////////////////////////////////////////////////////////////////////////////
/**
 * <b>rect</b> Dessine un rectangle plein. La couleur par défaut est noire.
 * Pour changer la couleur appeler au préalable la fonction fill.
 * @see #fill(Uint8, Uint8, Uint8)
 * @author SD
 * @param <b>posx</b> : Position x en pixels du coin supérieur gauche
 * @param <b>posy</b> : Position y en pixels du coin supérieur gauche
 * @param <b>width</b> : Largeur en pixels
 * @param <b>height</b> : Hauteur en pixels
*/
void rect(unsigned posx, unsigned posy, unsigned width, unsigned height) {
    SDL_Surface *s;

    /* Creating the surface. */
    s = SDL_CreateRGBSurface(0, width, height, 32, 0, 0, 0, 0);

    /* Filling the surface with red color. */
    SDL_FillRect(s, NULL, SDL_MapRGB(s->format, __graphique.fill_red,
                                                __graphique.fill_green, 
                                                __graphique.fill_blue));
                        
    SDL_Rect position;
    position.x = posx;
    position.y = posy;
    
    SDL_BlitSurface(s, NULL, __graphique.screen, &position);
}


////////////////////////////////////////////////////////////////////////////////
/**
 * <b>refresWindow</b> Met à jour l'affichage de la fenêtre.
 * @author SD
*/
void refreshWindow() {
    SDL_UpdateWindowSurface(__graphique.pWindow);
}


////////////////////////////////////////////////////////////////////////////////
/**
 * <b>fill</b> Change la couleur dans laquelle sont dessinées les formes selon 
 * les composantes RVB passées en paramètres.
 * @author SD
 * @param <b>red</b> : Composante rouge (0 - 255)
 * @param <b>green</b> : Composante verte (0 - 255)
 * @param <b>blue</b> : Composante bleue (0 - 255)
*/
void fill(Uint8 red, Uint8 green, Uint8 blue) {
    __graphique.fill_red = red; 
    __graphique.fill_green = green;
    __graphique.fill_blue = blue;
    
    __graphique.fill_color = blue + green * 256 + red * 65536;
}


////////////////////////////////////////////////////////////////////////////////
/**
 * <b>ligneHorizontale</b> Dessine une ligne horizontale. La couleur par défaut est noire.
 * Pour changer la couleur appeler au préalable la fonction fill.
 * @author SD
 * @param <b>posx</b> : Position x en pixels du point gauche
 * @param <b>posy</b> : Position y en pixels du point gauche
 * @param <b>width</b> : Largeur en pixels
*/
void ligneHorizontale(unsigned posx, unsigned posy, unsigned width)
{
    SDL_Rect r;
 
    r.x = posx;
    r.y = posy;
    r.w = width;
    r.h = 1;
 
    SDL_FillRect(__graphique.screen, &r, __graphique.fill_color);
}


////////////////////////////////////////////////////////////////////////////////
/**
 * <b>ligneVerticale</b> Dessine une ligne verticale. La couleur par défaut est noire.
 * Pour changer la couleur appeler au préalable la fonction fill.
 * @author SD
 * @param <b>posx</b> : Position x en pixels du point gauche
 * @param <b>posy</b> : Position y en pixels du point gauche
 * @param <b>height</b> : Hauteur en pixels
*/
void ligneVerticale(unsigned posx, unsigned posy, unsigned height)
{
    SDL_Rect r;
 
    r.x = posx;
    r.y = posy;
    r.w = 1;
    r.h = height;
 
    SDL_FillRect(__graphique.screen, &r, __graphique.fill_color);
}


////////////////////////////////////////////////////////////////////////////////
/**
 * <b>cercle</b> Dessine un cercel. La couleur par défaut est noire.
 * Pour changer la couleur appeler au préalable la fonction fill.
 * @author SD
 * @param <b>posx</b> : Position x en pixels du centre du cercle
 * @param <b>posy</b> : Position y en pixels du centre du cercle
 * @param <b>rayon</b> : Rayon du cercle en pixels
*/
void cercle(unsigned posx, unsigned posy, unsigned rayon)
{
    int d, y, x;
 
    d = 3 - (2 * rayon);
    x = 0;
    y = rayon;
 
    while (y >= x) {
        _setPixelVerif(posx + x, posy + y, __graphique.fill_color);
        _setPixelVerif(posx + y, posy + x, __graphique.fill_color);
        _setPixelVerif(posx - x, posy + y, __graphique.fill_color);
        _setPixelVerif(posx - y, posy + x, __graphique.fill_color);
        _setPixelVerif(posx + x, posy - y, __graphique.fill_color);
        _setPixelVerif(posx + y, posy - x, __graphique.fill_color);
        _setPixelVerif(posx - x, posy - y, __graphique.fill_color);
        _setPixelVerif(posx - y, posy - x, __graphique.fill_color);
 
        if (d < 0)
            d = d + (4 * x) + 6;
        else {
            d = d + 4 * (x - y) + 10;
            y--;
        }
 
        x++;
    }
}


////////////////////////////////////////////////////////////////////////////////
/**
 * <b>disque</b> Dessine un cercel. La couleur par défaut est noire.
 * Pour changer la couleur appeler au préalable la fonction fill.
 * @author SD
 * @param <b>posx</b> : Position x en pixels du centre du disque
 * @param <b>posy</b> : Position y en pixels du centre du disque
 * @param <b>rayon</b> : Rayon du disque en pixels
*/
void disque(unsigned posx, unsigned posy, unsigned rayon)
{
    int d, y, x;
 
    d = 3 - (2 * rayon);
    x = 0;
    y = rayon;
 
    while (y >= x) {
        ligneHorizontale(posx - x, posy - y, 2 * x + 1);
        ligneHorizontale(posx - x, posy + y, 2 * x + 1);
        ligneHorizontale(posx - y, posy - x, 2 * y + 1);
        ligneHorizontale(posx - y, posy + x, 2 * y + 1);
 
        if (d < 0)
            d = d + (4 * x) + 6;
        else {
            d = d + 4 * (x - y) + 10;
            y--;
        }
 
        x++;
    }
}


//////////////////////////////////////////////////////////////////////////////// 
/**
 * <b>ligne</b> Dessine une ligne entre deux points. La couleur par défaut est
 *  noire. Pour changer la couleur appeler au préalable la fonction fill.
 * @author SD
 * @param <b>x1</b> : Position x en pixels d'une extrémité de la ligne
 * @param <b>y1</b> : Position y en pixels d'une extrémité de la ligne
 * @param <b>x2</b> : Position x en pixels de l'autre extrémité de la ligne
 * @param <b>y2</b> : Position y en pixels de l'autre extrémité de la ligne
*/
void ligne(unsigned x1, unsigned y1, unsigned x2, unsigned y2)
{
  int d, dx, dy, aincr, bincr, xincr, yincr, x, y;
 
  if (abs(x2 - x1) < abs(y2 - y1)) {
    /* parcours par l'axe vertical */
 
    if (y1 > y2) {
      _echangerEntiers(&x1, &x2);
      _echangerEntiers(&y1, &y2);
    }
 
    xincr = x2 > x1 ? 1 : -1;
    dy = y2 - y1;
    dx = abs(x2 - x1);
    d = 2 * dx - dy;
    aincr = 2 * (dx - dy);
    bincr = 2 * dx;
    x = x1;
    y = y1;
 
    _setPixelVerif(x, y, __graphique.fill_color);
 
    for (y = y1+1; y <= y2; ++y) {
      if (d >= 0) {
	x += xincr;
	d += aincr;
      } else
	d += bincr;
 
      _setPixelVerif(x, y, __graphique.fill_color);
    }
 
  } else {
    /* parcours par l'axe horizontal */
    
    if (x1 > x2) {
      _echangerEntiers(&x1, &x2);
      _echangerEntiers(&y1, &y2);
    }
 
    yincr = y2 > y1 ? 1 : -1;
    dx = x2 - x1;
    dy = abs(y2 - y1);
    d = 2 * dy - dx;
    aincr = 2 * (dy - dx);
    bincr = 2 * dy;
    x = x1;
    y = y1;
 
    _setPixelVerif(x, y, __graphique.fill_color);
 
    for (x = x1+1; x <= x2; ++x) {
      if (d >= 0) {
	y += yincr;
	d += aincr;
      } else
	d += bincr;
 
      _setPixelVerif(x, y, __graphique.fill_color);
    }
  }    
}


////////////////////////////////////////////////////////////////////////////////
/**
 * <b>setPixel</b> Dessine un pixel. La couleur par défaut est noire. Pour 
 * changer la couleur appeler au préalable la fonction fill.
 * @author SD
 * @param <b>posx</b> : Postion x en pixels du pixel
 * @param <b>posy</b> : Postion y en pixels du pixel 
 */
void setPixel(unsigned posx, unsigned posy) {
    _setPixelVerif(posx, posy, __graphique.fill_color);
}


////////////////////////////////////////////////////////////////////////////////
void _setPixel(unsigned x, unsigned y, Uint32 coul)
{
    *((Uint32*)(__graphique.screen->pixels) + x + y * __graphique.screen->w) = coul;
}


////////////////////////////////////////////////////////////////////////////////
void _setPixelVerif(unsigned x, unsigned y, Uint32 coul)
{
    if (x >= 0 && x < __graphique.screen->w &&
                y >= 0 && y < __graphique.screen->h)
        _setPixel(x, y, coul);
}


////////////////////////////////////////////////////////////////////////////////
void _echangerEntiers(unsigned* x, unsigned* y)
{
  int t = *x;
  *x = *y;
  *y = t;
}


////////////////////////////////////////////////////////////////////////////////
Uint32 _poolEvent(Uint32 interval, void *param)
{
    SDL_Event event;
    SDL_UserEvent userevent;

    userevent.type = SDL_USEREVENT;
    userevent.code = 0;
    userevent.data1 = NULL;
    userevent.data2 = NULL;

    event.type = SDL_USEREVENT;
    event.user = userevent;

    SDL_PushEvent(&event);
    
    //printf("Dans le thread\n");

    return interval;
}