/*  fractal.c  draw trees */

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

static Display *dpy;
static Window window1;
static GC gc;
static Screen *screen;
static int screen_num;
static Colormap cmap;
static int status;
static XColor white_c, black_c, green_c, exact_c;
static int w1W = 600;
static int w1H = 500;
static int line_style = 0; /* LineSolid */
static int cap_style  = 2; /* CapRound */
static int join_style = 1; /* JoinRound */

int linewidth = 1;
float height      = 80.0;
float width       = 20.0;
float left_alpha  =  2.0;
float right_alpha =  2.2;
float left_angle  = 20.0;
float right_angle = 26.0;
int level_in      = 14;
float left_width_factor;
float right_width_factor;
float left_height_factor;
float right_height_factor;
int level; 
float x, y, x1, y01;
float turtle_x, turtle_y, turtle_r, turtle_theta;
static void generate(float x, float y, float width, float height,
                     float angle, int level);
static void turn(float angle);
static float point(float x1, float y01, float x2, float y2);
static void step(void);

static void doInitialize(void);          /* function prototypes used below */
static void doCreateWindows(void);
static void doCreateGraphicsContext(void);
static void doExpose(XEvent *eventP);
static void doHandleEvents(void);

/********************** The main program *******************************/
int main(int argc, char *argv[])
{
    /* may read command line */
    printf("height=%g, width=%g, left_alpha=%g, right_alpha=%g,\n left_angle=%g, right_angle=%g, level_in=%d \n",
    height, width, left_alpha, right_alpha, left_angle, right_angle, level_in);

    doInitialize();
    doHandleEvents(); /* this never returns */
    return 0;
}                                        

/***************** doInitialize **************************/
static void doInitialize(void)
{
  dpy = XOpenDisplay(0); 
  if (!dpy)
  {
    printf("Display not opened!\n");
    exit(1);
   }
  screen = XDefaultScreenOfDisplay(dpy);

  screen_num = XDefaultScreen(dpy);
  cmap = XDefaultColormap(dpy, screen_num);
  status = XAllocNamedColor(dpy, cmap, "white", &white_c, &exact_c);
  if (!status) printf("no white \n"); 
  status = XAllocNamedColor(dpy, cmap, "black", &black_c, &exact_c);
  if (!status) printf("no black \n"); 
  status = XAllocNamedColor(dpy, cmap, "green", &green_c, &exact_c);
  if (!status) printf("no green \n"); 

  doCreateWindows();

  doCreateGraphicsContext();

  XStoreName(dpy, window1, "fractal");

  XMapWindow(dpy, window1);
} 

/******* doCreateWindows *********/
static void doCreateWindows(void)
{   
  int window1X = 0;
  int window1Y = 0;
  XSetWindowAttributes xswa;
                   
  /* Create the window1 window */
  xswa.event_mask = ExposureMask | ButtonPressMask;
  xswa.background_pixel = XWhitePixelOfScreen(screen);

  window1 = XCreateWindow(dpy,
                          XRootWindowOfScreen(screen),
                          window1X, 
                          window1Y,
                          w1W,
                          w1H,
                          0,
                          XDefaultDepthOfScreen(screen),
                          InputOutput,
                          XDefaultVisualOfScreen(screen),
                          CWEventMask | CWBackPixel,
                          &xswa);
}

/******** Create the graphics context *********/    
static void doCreateGraphicsContext(void)
{                                                
  XGCValues xgcv;
    
  /* Create graphics context. */
  xgcv.background = XWhitePixelOfScreen(screen);
  xgcv.foreground = XBlackPixelOfScreen(screen);
  gc = XCreateGC(dpy, window1, GCForeground | GCBackground, &xgcv);   

  XSetForeground(dpy, gc, black_c.pixel);
  XSetLineAttributes(dpy, gc, linewidth, line_style, cap_style, join_style);


}


/****************** doHandleEvents ***********************/
static void doHandleEvents(void)
{
  XEvent event;         

  for ( ; ; )
  {
    XNextEvent(dpy, &event);
    switch (event.type)
    { 
      case Expose:
        doExpose(&event);
        break;
      case ButtonPress:
        exit(0); /* shut down */
    }
  }                                           
}

/***** draw in the window *****/
static void doExpose(XEvent *eventP)
{


  /* If this is an expose event on our window1 window,
     then write out the fractal. */

  if (eventP->xexpose.window != window1) return;
  XClearWindow(dpy, window1);

  /* continuing main from generate trees */
  level = level_in;
  left_width_factor   = pow(2, -1/left_alpha);
  right_width_factor  = pow(2, -1/right_alpha);
  left_height_factor  = pow(2, -2/(3*left_alpha));
  right_height_factor = pow(2, -2/(3*right_alpha));

  x = w1W/2.0;
  y = w1H/10.0;
  linewidth = width;
  XSetForeground(dpy, gc, black_c.pixel);
  XSetLineAttributes(dpy, gc, linewidth, line_style, cap_style, join_style);

  x1 = x;
  y01 = y+height;
  XDrawLine(dpy, window1, gc, (int)x, w1H-(int)y, (int)x1, w1H-(int)y01);
  turtle_r = height;
  turtle_theta = point(x, y, x1, y01);
  turtle_x = x;
  turtle_y = y;
  turn(left_angle);
  generate(x1, y01, left_width_factor*width,
	   left_height_factor*height, left_angle, level);
  turtle_theta = point(x, y, x1, y01);
  turn(-right_angle);
  generate(x1, y01, left_width_factor*width,
	   left_height_factor*height, right_angle, level);
}

static void generate(float x, float y, float width, float height,
                     float angle, int level)
{
    float x1, y01;

    turtle_x = x;
    turtle_y = y;
    turtle_r = height;
    step();
    x1 = turtle_x;
    y01 = turtle_y;
    linewidth = width;
    XSetLineAttributes(dpy, gc, linewidth, line_style, cap_style, join_style);
    level--;
    if(level<3)
    {
      XSetForeground(dpy, gc, green_c.pixel);
      XDrawLine(dpy, window1, gc, (int)x, w1H-(int)y, (int)x1, w1H-(int)y01);
    }
    else
    {
      XSetForeground(dpy, gc, black_c.pixel);
      XDrawLine(dpy, window1, gc, (int)x, w1H-(int)y, (int)x1, w1H-(int)y01);
    }
    if(level>0)
    {
	turtle_theta = point(x, y, x1, y01);
	turn(left_angle);
        generate(turtle_x, turtle_y, left_width_factor*width,
	         left_height_factor*height, left_angle, level);
        turtle_theta = point(x, y, x1, y01);
        turn(-right_angle);
        generate(x1,y01, left_width_factor*width,
	         left_height_factor*height, right_angle, level);

    }
}

static void turn(float angle)
{
    turtle_theta += angle;
}

static float point(float x1, float y01, float x2, float y2)
{
    float theta;

    if((x2-x1)==0.0)
    {
	if(y2>y01) theta=90.0;
        else       theta=270.0;
    }
    else
    {
	theta=atan((y2-y01)/(x2-x1))*57.295779;
    }
    if(x1>x2) theta += 180.0;
    return theta;
}

static void step(void)
{
    turtle_x += turtle_r*cos(turtle_theta*0.017453292);
    turtle_y += turtle_r*sin(turtle_theta*0.017453292);
}

