#include <cstdio>
#include <cstdlib>
#include <string>
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include <FMOD/fmod.h>
#include <iostream>
#include <cmath>
#include <ctime>

using namespace std;

class Complexe {
    private:
        double re;
        double im;
    public:
	    
        Complexe(double x = 0, double y = 0) : re(x), im(y)  { }
        
        void setRe(double reel) { re = reel; }
        void setIm(double imaginaire) { im = imaginaire; }
        double getRe() const { return re; }
        double getIm() const { return im; }
        double Norme() const { return sqrt(getRe()*getRe() + getIm()*getIm()); }
        Complexe Addition(const Complexe& c1, const Complexe& c2) {
	        Complexe temp;
	        temp.re = c1.getRe() + c2.getRe();
	        temp.im = c1.getIm() + c2.getIm();
	        return temp;
		}
		Complexe Multiplication(const Complexe &c1, const Complexe& c2) {
	        Complexe temp;
	        temp.re = c1.getRe()*c2.getRe() - c1.getIm()*c2.getIm();
	        temp.im = c1.getRe()*c2.getIm() + c1.getIm()*c2.getRe();
	        return temp;
		}
		Complexe Soustraction(const Complexe& c1, const Complexe& c2) {
	        Complexe temp;
	        temp.re = c1.getRe() - c2.getRe();
	        temp.im = c1.getIm() - c2.getIm();
	        return temp;
		}
		Complexe MulParReel(const Complexe &c1, double reel) {
	        Complexe temp;
	        temp.re = c1.getRe() * reel;
	        temp.im = c1.getIm() * reel;
	        return temp;
		}
		Complexe Racine(const Complexe &c1) {
	        Complexe temp;
			double a = 0, b = 0;
	        a = sqrt((c1.Norme() + c1.getRe())/2.);
	        if(c1.getIm() >= 0.) 
                b = sqrt((c1.Norme() - c1.getRe())/2.);
			else b = -sqrt((c1.Norme() - c1.getRe())/2.);
			temp.re = a;
			temp.im = b;
			return temp;
		}
};

int aleatoire(int min, int max) {
    int nb = (int) (min + ((float) rand() / RAND_MAX * (max - min + 1)));
    if(nb == 0)
        return -1;
    else return 1;
}

int arrondi(double nb) {
    if(nb - (int)nb >= 0.5)
        return (int)nb+1;
    else return (int)nb;
}

Complexe dynamique[1500];

Complexe f(Complexe z, Complexe c) {
    Complexe temp;
    return (temp.Addition(temp.Multiplication(z,z),c));
}

Complexe Julia(int n, Complexe z0, Complexe c) {
    if(dynamique[n].getRe() != -100000 && dynamique[n].getIm() != -100000) {
		//printf("\tdyn %lf %lf\n", dynamique[n].getRe(), dynamique[n].getIm()); 
        return dynamique[n];
    }
    Complexe temp;
    if(n == 0)
        return z0;
    else {
		dynamique[n] = (temp.Addition(temp.Multiplication(Julia(n-1, z0, c),Julia(n-1, z0, c)), c));
        return dynamique[n];
		return (temp.Addition(temp.Multiplication(Julia(n-1, z0, c),Julia(n-1, z0, c)), c));
	}
}

//moveto, lineto -> fait une ligne de x y moveto à x y lineto
//plot -> dessine le point aux coordonnées x y

/*
void representeJulia(Complexe c) {
    int x0 = 640/2;
    int y0 = 480/2;
    
	Complexe temp;
    
	Complexe z(0.2, 0.2);
    
    for(int i = 1; i <= 10000; i++) {
        z = temp.Soustraction(z, c);
        z = temp.Racine(z);
        z = temp.MulParReel(z, aleatoire(-1, 1));
        
		if(i > 50) {
	        position.x = arrondi((z.getRe()+2)*(640/4));
	        position.y = arrondi((z.getIm()+2)*(480/4));
            SDL_BlitSurface(pPoint, NULL, screen, position);    
		}
	}

}
*/

int main(int argc, char **argv)
{
 	srand(time(NULL));	
	SDL_Surface *screen = NULL;
	SDL_Surface *pPoint = NULL;
	SDL_Rect position;
    SDL_Event event;
    bool loop = true;
    SDL_Init(SDL_INIT_VIDEO);
    screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE);
    pPoint = SDL_CreateRGBSurface(SDL_HWSURFACE, 1, 1,32, 0, 0, 0, 0);
    SDL_WM_SetCaption("Fractale de Julia", NULL);
    
    SDL_Surface *choix = NULL;
    choix = SDL_LoadBMP("images/choix.bmp");
    SDL_Rect positionChoix;
    positionChoix.x = 0;
    positionChoix.y = 0;
    bool presentation = true;
    
    Complexe cInit;
    
    while(presentation) {
	    Etiquette:
	    SDL_ShowCursor(SDL_DISABLE);
	    SDL_PollEvent(&event);
	    switch(event.type) {
            case SDL_QUIT:
				 SDL_Quit();
				 exit(0);
				 break;
            case SDL_KEYDOWN:
				 switch(event.key.keysym.sym) {
			         case SDLK_ESCAPE:
					 	  SDL_Quit();
					 	  exit(0);
					 	  break;
	 	             case SDLK_1:
					 	  cInit.setRe(-0.12);
					 	  cInit.setIm(0.9);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_2:
					 	  cInit.setRe(0);
					 	  cInit.setIm(1);
					 	  presentation = false;
				 	      break;
	 	             case SDLK_3:
					 	  cInit.setRe(0.2);
					 	  cInit.setIm(0);
					 	  presentation = false;
					 	  break;
                     case SDLK_4:
					 	  cInit.setRe(-1.8);
    					  cInit.setIm(0);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_5:
					 	  cInit.setRe(0.3);
					 	  cInit.setIm(0.5);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_6:
					 	  cInit.setRe(-0.9);
					 	  cInit.setIm(0.12);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_7:
					 	  cInit.setRe(-0.745);
					 	  cInit.setIm(0.1);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_8:
					 	  cInit.setRe(-0.7);
					 	  cInit.setIm(-0.2);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_9:
					 	  cInit.setRe(0.32);
					 	  cInit.setIm(0.043);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_a:
					 	  cInit.setRe(-0.0986);
					 	  cInit.setIm(-0.65186);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_b:
					 	  cInit.setRe(-0.7927);
					 	  cInit.setIm(0.1609);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_c:
					 	  cInit.setRe(-1.1380);
					 	  cInit.setIm(0.2403);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_d:
					 	  cInit.setRe(-0.1225);
					 	  cInit.setIm(0.7449);
					 	  presentation = false;
					 	  break;
                     case SDLK_e:
					 	  cInit.setRe(-0.3380);
					 	  cInit.setIm(-0.6230);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_f:
					 	  cInit.setRe(-0.577);
					 	  cInit.setIm(0.478);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_g:
					 	  cInit.setRe(-1);
					 	  cInit.setIm(0);
					 	  presentation = false;
					 	  break;
                     case SDLK_KP1:
					 	  cInit.setRe(-0.12);
					 	  cInit.setIm(0.9);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_KP2:
					 	  cInit.setRe(0);
					 	  cInit.setIm(1);
					 	  presentation = false;
				 	      break;
	 	             case SDLK_KP3:
					 	  cInit.setRe(0.2);
					 	  cInit.setIm(0);
					 	  presentation = false;
					 	  break;
                     case SDLK_KP4:
					 	  cInit.setRe(-1.8);
    					  cInit.setIm(0);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_KP5:
					 	  cInit.setRe(0.3);
					 	  cInit.setIm(0.5);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_KP6:
					 	  cInit.setRe(-0.9);
					 	  cInit.setIm(0.12);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_KP7:
					 	  cInit.setRe(-0.745);
					 	  cInit.setIm(0.1);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_KP8:
					 	  cInit.setRe(-0.7);
					 	  cInit.setIm(-0.2);
					 	  presentation = false;
					 	  break;
	 	             case SDLK_KP9:
					 	  cInit.setRe(0.32);
					 	  cInit.setIm(0.043);
					 	  presentation = false;
					 	  break;
				 }
				 break;
		}
		SDL_BlitSurface(choix, NULL, screen, &positionChoix);
		SDL_Flip(screen);
		SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
	}
    
    SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255)); 
    SDL_Flip(screen);
   
    //Complexe cInit(-0.12,0.9);
    //Complexe cInit(0,1);
    //Complexe cInit(0.2,0);
    //Complexe cInit(-1.8,0);
    //Complexe cInit(0.3,0.5);
    //Complexe cInit(0.8,0.2);
    //Complexe cInit(-0.745,0.1);
    //Complexe cInit(-0.7,-0.2);
    //Complexe cInit(0.32,0.043);
    //Complexe cInit(-0.0986,-0.65186);
    //Complexe cInit(-0.7927,0.1609);
    //Complexe cInit(-1.1380,0.2403);
    //Complexe cInit(-0.1225,0.7449);
    //Complexe cInit(-0.3380,-0.6230);
    //Complexe cInit(-0.577,0.478);
 
    
    //representeJulia(cInit); 
    
	Complexe temp;
    
	Complexe z(0.2, 0.2);
    
    for(unsigned long long i = 1; i <= 1000000; i++) {
        z = temp.Soustraction(z, cInit);
        z = temp.Racine(z);
        z = temp.MulParReel(z, aleatoire(-1, 1));
        
		if(i > 50) {
	        position.x = arrondi((z.getRe()+2)*(640/4));
	        position.y = arrondi((z.getIm()+2)*(480/4));
            SDL_BlitSurface(pPoint, NULL, screen, &position);    
		}
	}
   
   SDL_Flip(screen); 

    while(loop) {
        SDL_PollEvent(&event);
        switch(event.type) {
            case SDL_QUIT:
                loop = false;
                break;
            case SDL_KEYDOWN:
                switch(event.key.keysym.sym) {
                    case SDLK_RETURN:
			            presentation = true;
			            goto Etiquette;
		                loop = false;
		                break;
                }
                break;
        }
    }

    SDL_Quit();	
	return 0;
}
