/* Sequential Mandlebrot program */


#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <sys/time.h>

#define		X_RESN	800       /* x resolution */
#define		Y_RESN	800       /* y resolution */

#define		DELAY   10	  /* delay factor: to make the program
                                    run slower and give more interesting
                                    timing result */

#define		HOLDSCREEN   1	  /* Time to show the final image on screen
                                     before terminate */ 
                                     
                                     
#define  H_MAX    360   /* HSB color space constant */
#define  S_MAX    256
#define  L_MAX    256
#define  RGBMAX   255   /* R,G, and B vary over 0->RGBMAX */

unsigned long HSLtoRGB(int H,int S,int L)
{
  double fH = (double)(H) / H_MAX;
  double fS = (double)(S) / S_MAX;
  double fL = (double)(L) / L_MAX;
  double fR,fG,fB;
  double temp1,temp2;
  double Rtemp3,Gtemp3,Btemp3;
  int R,G,B;
  if (fS == 0.0) {
    fR = fB = fG = fL;
  } else {
    if (fL < 0.5) temp2 = fL * (1.0 + fS);
    else temp2 = fL + fS - fL * fS;
    temp1 = 2.0 * fL - temp2;
    
    Rtemp3 = fH + 1.0/3.0;
    Gtemp3 = fH;
    Btemp3 = fH - 1.0/3.0;
    if (Rtemp3 > 1.0) Rtemp3 -= 1.0; else if (Rtemp3 < 0.0) Rtemp3 += 1.0;
    if (Gtemp3 > 1.0) Gtemp3 -= 1.0; else if (Gtemp3 < 0.0) Gtemp3 += 1.0;
    if (Btemp3 > 1.0) Btemp3 -= 1.0; else if (Btemp3 < 0.0) Btemp3 += 1.0;
    
    if (6.0 * Rtemp3 < 1.0) fR = temp1+(temp2-temp1)*6.0*Rtemp3;
    else if (2.0 * Rtemp3 < 1.0) fR = temp2;
    else if (3.0 * Rtemp3 < 2.0) fR = temp1+(temp2-temp1)*((2.0/3.0)-Rtemp3)*6.0;
    else fR = temp1;
  
    if (6.0 * Gtemp3 < 1.0) fG = temp1+(temp2-temp1)*6.0*Gtemp3;
    else if (2.0 * Gtemp3 < 1.0) fG = temp2;
    else if (3.0 * Gtemp3 < 2.0) fG = temp1+(temp2-temp1)*((2.0/3.0)-Gtemp3)*6.0;
    else fG = temp1;
  
    if (6.0 * Btemp3 < 1.0) fB = temp1+(temp2-temp1)*6.0*Btemp3;
    else if (2.0 * Btemp3 < 1.0) fB = temp2;
    else if (3.0 * Btemp3 < 2.0) fB = temp1+(temp2-temp1)*((2.0/3.0)-Btemp3)*6.0;
    else fB = temp1;
  }
  
  R = (int)(fR * (double)(RGBMAX));
  G = (int)(fG * (double)(RGBMAX));
  B = (int)(fB * (double)(RGBMAX));
  
  R = R & 0xFF;
  G = G & 0xFF;
  B = B & 0xFF;
  return (unsigned long)( (R << 16) + (G << 8) + B);
}
                                     
                                     
typedef struct complextype
	{
        double real, imag;
	} Compl;

struct timeval starttime, stoptime, elapsetime;

int main (int argc, char **argv)
{
	Window		win;                            /* initialization for a window */
	unsigned
	int             width, height,                  /* window size */
                        x, y,                           /* window position */
                        border_width,                   /*border width in pixels */
                        display_width, display_height,  /* size of screen */
                        screen;                         /* which screen */

	char            *window_name = "Mandelbrot Set", *display_name = NULL;
	GC              gc;
	unsigned
	long		valuemask = 0;
	XGCValues	values;
	Display		*display;
	XSizeHints	size_hints;
	Pixmap		bitmap;
	XPoint		points[800];
	FILE		*fp, *fopen ();
	char		str[100];
        int          color;
	
	XSetWindowAttributes attr[1];

       /* Mandlebrot variables */
        int i, j, k;
        Compl	z, c;
        double 	lengthsq, temp;
        
       /* precompute color value*/         
        unsigned long colorCache[H_MAX];
        for (k = 0;k < H_MAX;k++) {
          double d,l;
          d = ((1.0 * k) / 359.0);
          d = (1.0 - d * d);
          l = (int)(d * 128.0);
          colorCache[k] = HSLtoRGB(k,204,l);
        }
        

        double center_x, center_y;
        double zoom;

        if (argc != 4) {
          printf("Usage: mandelbrot center_x center_y zoom_factor\n");
          printf("center_x and center_y are between -2.0 to 2.0.\n");
          printf("The top-right corner is (2.0, 2.0) and the bottom-left corner is (-2.0, -2.0)\n");
          printf("The zoom_facter is greater than 1.0\n");
          printf("Example: mandelbrot -1.76 0.0 80\n");
          printf("Example: mandelbrot -.7451 .1125 2000\n");
          printf("Example: mandelbrot -.7451 .1125 5000\n");
          printf("Example: mandelbrot -.74541 .1125 1000000\n");
          printf("Example: mandelbrot -.745415 .11251 10000000\n");
          center_x = 0.0;
          center_y = 0.0;
          zoom = 1.0;
        }
        else {
          printf("%s %s %s\n",argv[1],argv[2],argv[3]);
          center_x = (double)atof(argv[1]);
          center_y = (double)atof(argv[2]);
          zoom = (double)atof(argv[3]);
          printf("center = %f,%f zoom = %f\n",center_x,center_y,zoom);
        }
        
          
 
	/* connect to Xserver */

	if (  (display = XOpenDisplay (display_name)) == NULL ) {
	   fprintf (stderr, "drawon: cannot connect to X server %s\n",
				XDisplayName (display_name) );
	exit (-1);
	}
	
	/* get screen size */

	screen = DefaultScreen (display);
	display_width = DisplayWidth (display, screen);
	display_height = DisplayHeight (display, screen);

	/* set window size */

	width = X_RESN;
	height = Y_RESN;

	/* set window position */

	x = 0;
	y = 0;

        /* create opaque window */

	border_width = 4;

	win = XCreateSimpleWindow (display, RootWindow (display, screen),
				x, y, width, height, border_width, 
				BlackPixel (display, screen), WhitePixel (display, screen));

	size_hints.flags = USPosition|USSize;
	size_hints.x = x;
	size_hints.y = y;
	size_hints.width = width;
	size_hints.height = height;
	size_hints.min_width = 300;
	size_hints.min_height = 300;
	
	XSetNormalHints (display, win, &size_hints);
	XStoreName(display, win, window_name);

        /* create graphics context */

	gc = XCreateGC (display, win, valuemask, &values);

	XSetBackground (display, gc, WhitePixel (display, screen));
	XSetForeground (display, gc, BlackPixel (display, screen));
	XSetLineAttributes (display, gc, 1, LineSolid, CapRound, JoinRound);

	attr[0].backing_store = Always;
	attr[0].backing_planes = 1;
	attr[0].backing_pixel = BlackPixel(display, screen);

	XChangeWindowAttributes(display, win, CWBackingStore | CWBackingPlanes | CWBackingPixel, attr);

	XMapWindow (display, win);
	XSync(display, 0);
      	 
        /* Calculate and draw points */
        gettimeofday(&starttime, NULL);
        for(i=0; i < X_RESN; i++) 
        for(j=0; j < Y_RESN; j++) {
          z.real = 0.0;
          z.imag = 0.0;
          c.real = ((double) j - 400.0)/200.0;		/* scale factors for 800 x 800 window */
          c.imag = -((double)i - 400.0)/200.0;
          c.real = c.real/zoom+center_x;
          c.imag = c.imag/zoom+center_y;
          k = 0;

          do  {                                             /* iterate for pixel color */
            temp = z.real*z.real - z.imag*z.imag + c.real;
            z.imag = 2.0*z.real*z.imag + c.imag;
            z.real = temp;
            lengthsq = z.real*z.real+z.imag*z.imag;
            k++;
          } while (lengthsq < 4.0 && k < H_MAX * DELAY);
          k = (k >= H_MAX * DELAY) ? H_MAX-1 : (k % H_MAX);

         XSetForeground (display, gc, colorCache[k]);
         XDrawPoint (display, win, gc, j, i);
        }

        gettimeofday(&stoptime, NULL);
        elapsetime.tv_usec = stoptime.tv_usec - starttime.tv_usec;
        elapsetime.tv_sec = stoptime.tv_sec - starttime.tv_sec;
        if (elapsetime.tv_usec<0) {
            elapsetime.tv_usec+=1000000;
            elapsetime.tv_sec--;
        }
        printf("elapse time %d sec %d usec\n", elapsetime.tv_sec, elapsetime.tv_usec);
	XFlush (display);
	sleep (HOLDSCREEN);

	/* Program Finished */

}