#define WIN32_LEAN_AND_MEAN

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

#include	"qe_ogl.h"




//---- ZSYSTEM TEST VERSION 0.1 ----------------------------------------------//

//---- ZSYSTEM TYPES ---------------------------------------------------------//

typedef struct _ZS_RAND {
  unsigned int a, b, c, d;
} ZS_RAND;



//---- ZSYSTEM DATA ----------------------------------------------------------//

int zs_cubes = 0;
int zs_buildings = 0;



//---- ZSYSTEM FUNCTIONS -----------------------------------------------------//

//---- ZS_Rand_Create --------------------------------------------------------//

ZS_RAND *ZS_Rand_Create(int seed)
{
  ZS_RAND *r = (ZS_RAND *)malloc(sizeof(ZS_RAND));
  if(!r) return NULL;
  r->a = seed + 0x87123631;
  r->b = seed * 3;
  r->c = seed + 0x72319631;
  r->d = seed ^ 0x24121531;
  return r;
}



//---- ZS_Rand_Delete --------------------------------------------------------//

void ZS_Rand_Delete(ZS_RAND *r)
{
  if(!r) return;
  free(r);
}



//---- ZS_Rand ---------------------------------------------------------------//

unsigned int ZS_Rand(ZS_RAND *r, unsigned int max)
{
  r->d ^= (r->c<<9) + (r->a>>7) + (r->d&r->a);

  r->a += r->b>>1;
  r->b += r->c^0xF0F0A05;
  r->c += r->d;
  r->d += 0x35136613;

  unsigned int v = ( (r->a*7 + r->c*6)^((r->b<<13)-(r->d>>3)) );

  if(max == 0) return v;

  return v % max;
}



//---- ZS_Rand_Tween ---------------------------------------------------------//

double ZS_Rand_Tween(ZS_RAND *r, double min, double max)
{
  double t = min + ZS_Rand(r, 1000000)*0.000001*(max-min);
  return t;
}



//---- ZSYSTEM OBJECT: CUBE --------------------------------------------------//

void Cube(double x1, double y1, double z1, double x2, double y2, double z2, double r, double g, double b)
{
  zs_cubes ++;
/*
  int rr, gg, bb;
  rr = zs_cubes*787;
  gg = (zs_cubes*17)^815;
  bb = ((zs_cubes*91)|812)/3;

  r = (rr%1000)*0.001;
  g = (gg%1000)*0.001;
  b = (bb%1000)*0.001;
*/


/*
  double d;
  d = 1.0-(0.0005*x1*0.0005*x1+0.0005*z1*0.0005*z1);

  if(d < 0) return;
  r *= d; g *= d; b *= d;
*/

  glColor3f(r, g, b);

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x1, y1, z1);
    glVertex3f(x1, y1, z2);
    glVertex3f(x2, y1, z2);
    glVertex3f(x2, y1, z1);
  glEnd();

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x2, y2, z1);
    glVertex3f(x2, y2, z2);
    glVertex3f(x1, y2, z2);
    glVertex3f(x1, y2, z1);
  glEnd();

  glColor3f(r*0.95, g*0.95, b*0.95);

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x1, y2, z1);
    glVertex3f(x1, y2, z2);
    glVertex3f(x1, y1, z2);
    glVertex3f(x1, y1, z1);
  glEnd();

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x2, y1, z1);
    glVertex3f(x2, y1, z2);
    glVertex3f(x2, y2, z2);
    glVertex3f(x2, y2, z1);
  glEnd();

  glColor3f(r*0.90, g*0.90, b*0.90);

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x2, y1, z1);
    glVertex3f(x2, y2, z1);
    glVertex3f(x1, y2, z1);
    glVertex3f(x1, y1, z1);
  glEnd();

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x1, y1, z2);
    glVertex3f(x1, y2, z2);
    glVertex3f(x2, y2, z2);
    glVertex3f(x2, y1, z2);
  glEnd();

}

void Cube2(double x1, double y1, double z1, double x2, double y2, double z2, double r, double g, double b)
{
  int i, j;

  for(j=-4; j<=4; j++)
    for(i=-4; i<=4; i++)
      Cube2(x1+i*500, y1, z1+j*500, x2+i*500, y2, z2+j*500, r, g, b);
}



//---- ZSYSTEM OBJECT: BUILDINGWINDOW ----------------------------------------//

void BuildingWindow(unsigned int seed, double x, double y, double z, int orientation)
{
  //- Draw sill

  Cube(x-0.6, y, z, x+0.6

}



//---- ZSYSTEM OBJECT: BUILDINGCUBE ------------------------------------------//

void BuildingCube(unsigned int seed, double x1, double y1, double z1, double x2, double y2, double z2, double lit)
{
  //- Draw building

  Cube(x1, y1, z1, x2, y2, z2, lit, lit, lit);

  //- Draw features

}



//---- ZSYSTEM OBJECT: BUILDING ----------------------------------------------//

void Building(unsigned int seed, double x1, double z1, double x2, double z2, double y)
{
  zs_buildings ++;

  ZS_RAND *rand = ZS_Rand_Create(seed);

  int i, j, ii, jj;
  double x, z, w, h, d;

  int r = ZS_Rand(rand, 100);

  double lit = ZS_Rand_Tween(rand, 0.8, 1.0);

  if((x2-x1) < 10 || (z2-z1) < 10) {

    //  Nothing - too small

  } else if(r < 40) {

    //  Basic building

    BuildingCube(ZS_Rand(rand, 0), x1, y, z1, x2, y+ZS_Rand_Tween(rand, 10, 50), z2, 0.8*lit);

  } else if(r < 60) {

    //  Building with array of blocks on top

    h = ZS_Rand_Tween(rand, 20, 70);
    BuildingCube(ZS_Rand(rand, 0), x1, y, z1, x2, y+h, z2, 0.75);

    ii = 1+ZS_Rand(rand, (int)((x2-x1)/3)-1);
    jj = 1+ZS_Rand(rand, (int)((z2-z1)/3)-1);

    w = (x2-x1)/ii;
    d = (z2-z1)/jj;

    for(j=0; j<jj; j++) {
      for(i=0; i<ii; i++) {
        if(ZS_Rand(rand, 100) < 95) Cube(x1+i*w+1, y+h, z1+j*d+1, x1+(i+1)*w-1, y+h+0.5, z1+(j+1)*d-1, 0.6*lit, 0.6*lit, 0.6*lit);
      }
    }

  } else if(r < 90) {

    //  Building with smaller block on top

    h = ZS_Rand_Tween(rand, 20, 70);
    BuildingCube(ZS_Rand(rand, 0), x1, y, z1, x2, y+h, z2, 0.65*lit);

    w = ZS_Rand_Tween(rand, (x2-x1)/6, (x2-x1)/3);
    d = ZS_Rand_Tween(rand, (z2-z1)/6, (z2-z1)/3);
    if(d > w*1.5) d = w*1.5;
    if(w > d*1.5) w = d*1.5;

    Building(ZS_Rand(rand, 0), x1+w, z1+d, x2-w, z2-d, y+h);

  } else {

    //  Building with array of long blocks

    h = ZS_Rand_Tween(rand, 10, 40);
    BuildingCube(ZS_Rand(rand, 0), x1, y, z1, x2, y+h, z2, 0.8*lit);

    if(x2-x1 > z2-z1) {

      ii = ZS_Rand(rand, (int)((x2-x1)/3));
      if(ii > 0) {
        w = (x2-x1)/ii;
        for(i=0; i<ii; i++) {
          Cube(x1+i*w+1, y+h, z1+1, x1+(i+1)*w-1, y+h+1, z2-1, 0.7*lit, 0.7*lit, 0.7*lit);
        }
      }

    } else {

      //  Line of buildings

      ii = ZS_Rand(rand, (int)((z2-z1)/3));

      if(ii > 0) {
        d = (z2-z1)/ii;
        for(i=0; i<ii; i++) {
          BuildingCube(ZS_Rand(rand, 0), x1+1, y+h, z1+i*d+1, x2-1, y+h+1, z1+(i+1)*d-1, 0.7*lit);
        }
      }

    }

  }

  ZS_Rand_Delete(rand);
}



//---- ZSYSTEM OBJECT: BLOCK -------------------------------------------------//

void Block(unsigned int seed, double x1, double z1, double x2, double z2)
{
  ZS_RAND *rand = ZS_Rand_Create(seed);

  //  Put base

  Cube(x1+3, 0, z1+3, x2-3, 0.1, z2-3, 0.5, 0.5, 0.5);

  //  Put buildings

  int r = ZS_Rand(rand, 100);

  if(r < 10) {

    //  No buildings

    Building(ZS_Rand(rand, 0), x1+8, z1+8, x2-8, z2-8, 0.1);

  } else if(r < 40) {

    //  Symmetrical array of buildings

    Building(ZS_Rand(rand, 0), x1+8, z1+8, (x2+x1)/2-4, z2-8, 0.1);
    Building(ZS_Rand(rand, 0), (x2+x1)/2+4, z1+8, x2-8, z2-8, 0.1);

  } else if(r < 80) {

    //  Randomly placed buildings

    Building(ZS_Rand(rand, 0), x1+8, z1+8, x2-8, z2-8, 0.1);

  } else {

    //  One big building

    Building(ZS_Rand(rand, 0), x1+8, z1+8, x2-8, z2-8, 0.1);

  }

  ZS_Rand_Delete(rand);
}



//---- ZSYSTEM OBJECT: ROAD --------------------------------------------------//

void Road(unsigned int seed, double x1, double z1, double x2, double z2)
{
  ZS_RAND *rand = ZS_Rand_Create(seed);

  //  Put base

  //  Put dashes

  int i, ii;
  double x, y, z;

  if(x2-x1 > z2-z1) {

    z=0.5*(z1+z2);
    for(x=x1; x<x2-1; x+=2) {
      Cube(x, 0, z-0.05, x+1, 0.01, z+0.05, 1, 1, 1);
    }

    for(z=z; z<z2; z+=0.4) {
      Cube(x1, 0, z, x1+0.10, 0.01, z+0.2, 1, 1, 1);
      Cube(x1+0.30, 0, z, x1+0.40, 0.01, z+0.2, 1, 1, 1);
    }

  } else {

    x=0.5*(x1+x2);
    for(z=z1; z<z2-1; z+=2) {
      Cube(x-0.05, 0, z, x+0.05, 0.01, z+1, 1, 1, 1);
    }

    for(x=x; x<x2; x+=0.4) {
      Cube(x, 0, z2-0.40, x+0.2, 0.01, z2-0.30, 1, 1, 1);
      Cube(x, 0, z2-0.10, x+0.2, 0.01, z2-0.00, 1, 1, 1);
    }

  }

  ZS_Rand_Delete(rand);
}



//---- ZSYSTEM OBJECT: SUBURB ------------------------------------------------//

void Suburb(unsigned int seed, double x1, double z1, double x2, double z2, int iter)
{
  ZS_RAND *rand = ZS_Rand_Create(seed);

  //- Check size

  BOOL smallx, smallz, smallrand;
  double x, z;

  smallx = (x2-x1) < 70;
  smallz = (z2-z1) < 70;
  smallrand = (ZS_Rand(rand, 100) < 20 && x2-x1 < 100 && z2-z1 < 100) || iter > 10;

  if(smallrand) {

    Block(ZS_Rand(rand, 0), x1, z1, x2, z2);

  } else if(smallx && smallz) {

    Block(ZS_Rand(rand, 0), x1, z1, x2, z2);

  } else if(smallx) {

    z = ZS_Rand_Tween(rand, z1+23, z2-23);
    Suburb(ZS_Rand(rand, 0), x1, z1, x2, z, iter+1);
    Suburb(ZS_Rand(rand, 0), x1, z, x2, z2, iter+1);

//    Road(ZS_Rand(rand, 0), x1+3, z-3, x2-3, z+3);

  } else if(smallz) {

    x = ZS_Rand_Tween(rand, x1+23, x2-23);
    Suburb(ZS_Rand(rand, 0), x1, z1, x, z2, iter+1);
    Suburb(ZS_Rand(rand, 0), x, z1, x2, z2, iter+1);

//    Road(ZS_Rand(rand, 0), x-3, z1+3, x+3, z2-3);

  } else {

    x = ZS_Rand_Tween(rand, x1+23, x2-15);
    z = ZS_Rand_Tween(rand, z1+23, z2-15);
    Suburb(ZS_Rand(rand, 0), x1, z1, x, z, iter+1);
    Suburb(ZS_Rand(rand, 0), x, z1, x2, z, iter+1);
    Suburb(ZS_Rand(rand, 0), x1, z, x, z2, iter+1);
    Suburb(ZS_Rand(rand, 0), x, z, x2, z2, iter+1);

//    Road(ZS_Rand(rand, 0), x-3, z1+3, x+3, z-3);
//    Road(ZS_Rand(rand, 0), x-3, z+3, x+3, z2-3);
//    Road(ZS_Rand(rand, 0), x1+3, z-3, x-3, z+3);
//    Road(ZS_Rand(rand, 0), x+3, z-3, x2-3, z+3);

    Cube(x-1, 0, z-1, x+1, 0.01, z+1, 0.4, 0.4, 0.4);
  }

  ZS_Rand_Delete(rand);
}



//---- ZSYSTEM OBJECT: CITY --------------------------------------------------//

void City(unsigned int seed, double x1, double z1, double x2, double z2)
{
  ZS_RAND *rand = ZS_Rand_Create(seed);

  //  Place floor poly

//  Cube(x1, -1, z1, x2, 0, z2, 0.1, 0.1, 0.1);

  //  Divide into blocks

  Suburb(ZS_Rand(rand, 0), x1, z1, x2, z2, 0);

  ZS_Rand_Delete(rand);
}



//---- OPENGL STUFF ----------------------------------------------------------//

void Initialise()
{

}



void GameDraw(HWND hwnd)
{
  static k=0;
  int citysize = 500;
//  k++;
 
  int i=0, j=0;

  zs_cubes = 0;
  zs_buildings = 0;

//  for(j=-2; j<=2; j++) for(i=-2; i<=2; i++)
    City(0xA749F86C + (i<<17) + (j<<5) + k, -citysize/2 + i*citysize, -citysize/2 + j*citysize, citysize/2 + i*citysize, citysize/2 + j*citysize);

  static int t = 0;
  if(GetTickCount()-t > 500) {
    t = GetTickCount();
    char buf[50];
    sprintf(buf, "City: %d buildings, %d cubes", zs_buildings, zs_cubes);
    SetWindowText(hwnd, buf);
  }

}


double oldr1=0, oldr2=0;

double r1=20/0.2, r2=-50/0.2;
double x=0, y=200, z=0;
int moving = 0;



void GameMain(HWND hwnd)
{
  if(oldr1 == r1 && oldr2 == r2 && !moving) return;
  oldr1 = r1; oldr2 = r2;

  static int s = 0;
  int t = GetTickCount();

  int timediff = t-s;
  if(timediff > 200) timediff = 200;
  s = t;

  double rr1 = r1, rr2 = r2;

  {
    double vx = 0, vy = 0, vz = -timediff*0.03*moving;

    double wx, wy, wz;

    wx = vx;
    wy = vy*cos(0.2*rr2*3.14159/180)-vz*sin(0.2*rr2*3.14159/180);
    wz = -(vy*sin(0.2*rr2*3.14159/180)+vz*cos(0.2*rr2*3.14159/180));

    vx = wx*cos(0.2*rr1*3.14159/180)-wz*sin(0.2*rr1*3.14159/180);
    vy = wy;
    vz = -(wx*sin(0.2*rr1*3.14159/180)+wz*cos(0.2*rr1*3.14159/180));

    x += vx; y += vy; z += vz;
  }

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	glRotatef(-r2*0.2, 1.0, 0.0, 0.0);
	glRotatef(-r1*0.2, 0.0, 1.0, 0.0);

	glTranslatef(-x, -y, -z);
	GameDraw(hwnd);

	SwapBuffers(gl_dc);
}



//---- END OF FILE -----------------------------------------------------------//

