/*
 * Fichier exemple de la 2ème partie du tutoriel SDL d'Anomaly
 * http://anomaly.developpez.com/tutoriel/sdl/partie2/
 * Ce fichier source est libre de droits.
 */

#include <stdlib.h>
#include <stdio.h>
#include "SDL.h"

enum {
  C_NOIR, C_BLEU_FONCE, C_VERT_FONCE, C_CYAN_FONCE, C_ROUGE_FONCE,
  C_MAGENTA_FONCE, C_OCRE, C_GRIS_CLAIR, C_GRIS, C_BLEU, C_VERT,
  C_CYAN, C_ROUGE, C_MAGENTA, C_JAUNE, C_BLANC,
  
  NB_COULEURS
};

SDL_Surface* affichage;
Uint32 couleurs[NB_COULEURS];

void initSDL(void)
{
  if (SDL_Init(SDL_INIT_VIDEO) < 0) {
    fprintf(stderr, "Erreur à l'initialisation de la SDL : %s\n", SDL_GetError());
    exit(EXIT_FAILURE);
  }

  atexit(SDL_Quit);
  affichage = SDL_SetVideoMode(800, 600, 32, SDL_SWSURFACE);

  if (affichage == NULL) {
    fprintf(stderr, "Impossible d'activer le mode graphique : %s\n", SDL_GetError());
    exit(EXIT_FAILURE);
  }

  SDL_WM_SetCaption("Figures géométriques avec la SDL", NULL);
}

void initCouleurs(void)
{
  couleurs[C_NOIR]          = SDL_MapRGB(affichage->format, 0x00, 0x00, 0x00);
  couleurs[C_BLEU_FONCE]    = SDL_MapRGB(affichage->format, 0x00, 0x00, 0x80);
  couleurs[C_VERT_FONCE]    = SDL_MapRGB(affichage->format, 0x00, 0x80, 0x00);
  couleurs[C_CYAN_FONCE]    = SDL_MapRGB(affichage->format, 0x00, 0x80, 0x80);
  couleurs[C_ROUGE_FONCE]   = SDL_MapRGB(affichage->format, 0x80, 0x00, 0x00);
  couleurs[C_MAGENTA_FONCE] = SDL_MapRGB(affichage->format, 0x80, 0x00, 0x80);
  couleurs[C_OCRE]          = SDL_MapRGB(affichage->format, 0x80, 0x80, 0x00);
  couleurs[C_GRIS_CLAIR]    = SDL_MapRGB(affichage->format, 0xC0, 0xC0, 0xC0);
  couleurs[C_GRIS]          = SDL_MapRGB(affichage->format, 0x80, 0x80, 0x80);
  couleurs[C_BLEU]          = SDL_MapRGB(affichage->format, 0x00, 0x00, 0xFF);
  couleurs[C_VERT]          = SDL_MapRGB(affichage->format, 0x00, 0xFF, 0x00);
  couleurs[C_CYAN]          = SDL_MapRGB(affichage->format, 0x00, 0xFF, 0xFF);
  couleurs[C_ROUGE]         = SDL_MapRGB(affichage->format, 0xFF, 0x00, 0x00);
  couleurs[C_MAGENTA]       = SDL_MapRGB(affichage->format, 0xFF, 0x00, 0xFF);
  couleurs[C_JAUNE]         = SDL_MapRGB(affichage->format, 0xFF, 0xFF, 0x00);
  couleurs[C_BLANC]         = SDL_MapRGB(affichage->format, 0xFF, 0xFF, 0xFF);
}

void attendreTouche(void)
{
  SDL_Event event;

  do
    SDL_WaitEvent(&event);
  while (event.type != SDL_QUIT && event.type != SDL_KEYDOWN);
}

void actualiser(void)
{
  SDL_UpdateRect(affichage, 0, 0, 0, 0);
}

void setPixel(int x, int y, Uint32 coul)
{
  *((Uint32*)(affichage->pixels) + x + y * affichage->w) = coul;
}

void setPixelVerif(int x, int y, Uint32 coul)
{
  if (x >= 0 && x < affichage->w &&
      y >= 0 && y < affichage->h)
    setPixel(x, y, coul);
}

void barre(int x, int y, int w, int h, Uint32 coul)
{
  SDL_Rect r;

  r.x = x;
  r.y = y;
  r.w = w;
  r.h = h;

  SDL_FillRect(affichage, &r, coul);
}

void ligneHorizontale(int x, int y, int w, Uint32 coul)
{
  SDL_Rect r;

  r.x = x;
  r.y = y;
  r.w = w;
  r.h = 1;

  SDL_FillRect(affichage, &r, coul);
}

void ligneVerticale(int x, int y, int h, Uint32 coul)
{
  SDL_Rect r;

  r.x = x;
  r.y = y;
  r.w = 1;
  r.h = h;

  SDL_FillRect(affichage, &r, coul);
}

void rectangle(int x, int y, int w, int h, Uint32 coul)
{
  ligneHorizontale(x, y, w, coul);
  ligneHorizontale(x, y + h - 1, w, coul);
  ligneVerticale(x, y + 1, h - 2, coul);
  ligneVerticale(x + w - 1, y + 1, h - 2, coul);
}

void effacerEcran(void)
{
  SDL_FillRect(affichage, NULL, couleurs[C_NOIR]);
}

void echangerEntiers(int* x, int* y)
{
  int t = *x;
  *x = *y;
  *y = t;
}

void ligne(int x1, int y1, int x2, int y2, Uint32 coul)
{
  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, coul);

    for (y = y1+1; y <= y2; ++y) {
      if (d >= 0) {
	x += xincr;
	d += aincr;
      } else
	d += bincr;

      setPixelVerif(x, y, coul);
    }

  } 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, coul);

    for (x = x1+1; x <= x2; ++x) {
      if (d >= 0) {
	y += yincr;
	d += aincr;
      } else
	d += bincr;

      setPixelVerif(x, y, coul);
    }
  }    
}

void cercle(int cx, int cy, int rayon, int coul)
{
  int d, y, x;

  d = 3 - (2 * rayon);
  x = 0;
  y = rayon;

  while (y >= x) {
    setPixelVerif(cx + x, cy + y, coul);
    setPixelVerif(cx + y, cy + x, coul);
    setPixelVerif(cx - x, cy + y, coul);
    setPixelVerif(cx - y, cy + x, coul);
    setPixelVerif(cx + x, cy - y, coul);
    setPixelVerif(cx + y, cy - x, coul);
    setPixelVerif(cx - x, cy - y, coul);
    setPixelVerif(cx - y, cy - x, coul);

    if (d < 0)
      d = d + (4 * x) + 6;
    else {
      d = d + 4 * (x - y) + 10;
      y--;
    }

    x++;
  }
}

void disque(int cx, int cy, int rayon, int coul)
{
  int d, y, x;

  d = 3 - (2 * rayon);
  x = 0;
  y = rayon;

  while (y >= x) {
    ligneHorizontale(cx - x, cy - y, 2 * x + 1, coul);
    ligneHorizontale(cx - x, cy + y, 2 * x + 1, coul);
    ligneHorizontale(cx - y, cy - x, 2 * y + 1, coul);
    ligneHorizontale(cx - y, cy + x, 2 * y + 1, coul);

    if (d < 0)
      d = d + (4 * x) + 6;
    else {
      d = d + 4 * (x - y) + 10;
      y--;
    }

    x++;
  }
}

void testLignesHV(void)
{
  /* test des limites lignes horizontales (rouges) */

  ligneHorizontale(-10, 180, 100, couleurs[C_ROUGE]);
  ligneHorizontale(0, 190, 100, couleurs[C_ROUGE]);
  ligneHorizontale(10, 200, 100, couleurs[C_ROUGE]);

  ligneHorizontale(690, 180, 100, couleurs[C_ROUGE]);
  ligneHorizontale(700, 190, 100, couleurs[C_ROUGE]);
  ligneHorizontale(710, 200, 100, couleurs[C_ROUGE]);

  ligneHorizontale(-10, 210, 900, couleurs[C_ROUGE]);

  /* test des limites ligne verticales (jaunes) */

  ligneVerticale(180, -10, 100, couleurs[C_JAUNE]);
  ligneVerticale(190, 0, 100, couleurs[C_JAUNE]);
  ligneVerticale(200, 10, 100, couleurs[C_JAUNE]);

  ligneVerticale(180, 490, 100, couleurs[C_JAUNE]);
  ligneVerticale(190, 500, 100, couleurs[C_JAUNE]);
  ligneVerticale(200, 510, 100, couleurs[C_JAUNE]);

  ligneVerticale(210, -10, 900, couleurs[C_JAUNE]);

  /* lignes complètement hors limites (gris) */

  ligneHorizontale(50, -50, 200, couleurs[C_GRIS]);
  ligneHorizontale(50, 700, 200, couleurs[C_GRIS]);
  ligneVerticale(-50, 50, 200, couleurs[C_GRIS]);
  ligneVerticale(850, 50, 200, couleurs[C_GRIS]);
}

void testLignesQ(void)
{
  /* rectangle (gris) pour les lignes quelconques */

  rectangle(500, 300, 201, 201, couleurs[C_GRIS]);

  /* lignes quelconques 0-45° (blanches) */

  ligne(600, 400, 700, 400, couleurs[C_BLANC]);
  ligne(600, 400, 700, 380, couleurs[C_BLANC]);
  ligne(600, 400, 700, 360, couleurs[C_BLANC]);
  ligne(600, 400, 700, 340, couleurs[C_BLANC]);
  ligne(600, 400, 700, 320, couleurs[C_BLANC]);
  ligne(600, 400, 700, 300, couleurs[C_BLANC]);

  /* lignes quelconques 45-90° (vertes) */

  ligne(600, 400, 680, 300, couleurs[C_VERT]);
  ligne(600, 400, 660, 300, couleurs[C_VERT]);
  ligne(600, 400, 640, 300, couleurs[C_VERT]);
  ligne(600, 400, 620, 300, couleurs[C_VERT]);
  ligne(600, 400, 600, 300, couleurs[C_VERT]);

  /* lignes quelconques 90-135° (bleues) */

  ligne(600, 400, 580, 300, couleurs[C_BLEU]);
  ligne(600, 400, 560, 300, couleurs[C_BLEU]);
  ligne(600, 400, 540, 300, couleurs[C_BLEU]);
  ligne(600, 400, 520, 300, couleurs[C_BLEU]);
  ligne(600, 400, 500, 300, couleurs[C_BLEU]);

  /* lignes quelconques 135-180° (magentas) */

  ligne(600, 400, 500, 320, couleurs[C_MAGENTA]);
  ligne(600, 400, 500, 340, couleurs[C_MAGENTA]);
  ligne(600, 400, 500, 360, couleurs[C_MAGENTA]);
  ligne(600, 400, 500, 380, couleurs[C_MAGENTA]);
  ligne(600, 400, 500, 400, couleurs[C_MAGENTA]);

  /* lignes quelconques 180°-225° (jaunes) */

  ligne(600, 400, 500, 420, couleurs[C_JAUNE]);
  ligne(600, 400, 500, 440, couleurs[C_JAUNE]);
  ligne(600, 400, 500, 460, couleurs[C_JAUNE]);
  ligne(600, 400, 500, 480, couleurs[C_JAUNE]);
  ligne(600, 400, 500, 500, couleurs[C_JAUNE]);

  /* lignes quelconques 225°-270° (rouges) */

  ligne(600, 400, 520, 500, couleurs[C_ROUGE]);
  ligne(600, 400, 540, 500, couleurs[C_ROUGE]);
  ligne(600, 400, 560, 500, couleurs[C_ROUGE]);
  ligne(600, 400, 580, 500, couleurs[C_ROUGE]);
  ligne(600, 400, 600, 500, couleurs[C_ROUGE]);

  /* lignes quelconques 270°-315° (cyan) */

  ligne(600, 400, 620, 500, couleurs[C_CYAN]);
  ligne(600, 400, 640, 500, couleurs[C_CYAN]);
  ligne(600, 400, 660, 500, couleurs[C_CYAN]);
  ligne(600, 400, 680, 500, couleurs[C_CYAN]);
  ligne(600, 400, 700, 500, couleurs[C_CYAN]);

  /* lignes quelconques 315°-360° (ocre) */

  ligne(600, 400, 700, 480, couleurs[C_OCRE]);
  ligne(600, 400, 700, 460, couleurs[C_OCRE]);
  ligne(600, 400, 700, 440, couleurs[C_OCRE]);
  ligne(600, 400, 700, 420, couleurs[C_OCRE]);

  /* lignes quelconques hors limites (gris) */

  ligne(750, 500, 900, 550, couleurs[C_GRIS]);
  ligne(700, 700, 750, 550, couleurs[C_GRIS]);
  ligne(-50, 500, 50, 520, couleurs[C_GRIS]);
  ligne(650, 60, 690, -20, couleurs[C_GRIS]);
}

void testRectangles(void)
{
  /* rectangle entiers (blancs) */

  rectangle(600, 520, 50, 30, couleurs[C_BLANC]);
  barre(605, 525, 40, 20, couleurs[C_BLANC]);

  rectangle(660, 520, 30, 50, couleurs[C_BLANC]);
  barre(665, 525, 20, 40, couleurs[C_BLANC]);

  /* rectangle rouge avec bordure jaune */

  rectangle(350, 520, 60, 40, couleurs[C_JAUNE]);
  barre(351, 521, 58, 38, couleurs[C_ROUGE]);

  /* rectangles coupés (gris) */

  rectangle(600, 590, 50, 30, couleurs[C_GRIS]);
  barre(605, 595, 40, 20, couleurs[C_GRIS]);

  rectangle(780, 550, 50, 30, couleurs[C_GRIS]);
  barre(785, 555, 40, 20, couleurs[C_GRIS]);

  rectangle(-20, 540, 50, 30, couleurs[C_GRIS]);
  barre(-15, 545, 40, 20, couleurs[C_GRIS]);

  rectangle(700, -20, 50, 30, couleurs[C_GRIS]);
  barre(705, -15, 40, 20, couleurs[C_GRIS]);
}

void testCercles(void)
{
  cercle(200, 200, 10, couleurs[C_BLANC]);
  cercle(200, 200, 30, couleurs[C_JAUNE]);
  cercle(200, 200, 60, couleurs[C_ROUGE]);
  cercle(200, 200, 100, couleurs[C_VERT]);
  cercle(200, 200, 150, couleurs[C_BLEU]);
  cercle(200, 200, 220, couleurs[C_GRIS]);
  cercle(200, 200, 650, couleurs[C_GRIS]);

  rectangle(80, 440, 151, 151, couleurs[C_JAUNE]);
  cercle(155, 515, 75, couleurs[C_ROUGE]);

  disque(600, 200, 150, couleurs[C_BLEU]);
  disque(600, 200, 100, couleurs[C_VERT]);
  disque(600, 200, 60, couleurs[C_ROUGE]);
  disque(600, 200, 30, couleurs[C_JAUNE]);
  disque(600, 200, 10, couleurs[C_BLANC]);

  rectangle(530, 440, 151, 151, couleurs[C_JAUNE]);
  disque(605, 515, 75, couleurs[C_ROUGE]);

  cercle(400, 400, 50, couleurs[C_JAUNE]);
  disque(400, 400, 50, couleurs[C_ROUGE]);

  disque(400, 520, 50, couleurs[C_ROUGE]);
  cercle(400, 520, 50, couleurs[C_JAUNE]);
}

int main(int argc, char** argv)
{
  initSDL();
  initCouleurs();

  testLignesHV();
  testLignesQ();
  testRectangles();

  actualiser();
  attendreTouche();
  effacerEcran();

  testCercles();

  actualiser();
  attendreTouche();

  return EXIT_SUCCESS;
}
