/***************************************************************************
                          main.cpp  -  description
                             -------------------
    begin                : Sun Aug 10 19:31:53 EDT 2003
    written by           :  jared bruni
    email                :  jared@lostsidedead.com
    to compile           :  g++ main.cpp -B /usr/X11R6/lib -o mp3d -lX11 -lXi -lXmu -lglut -lGL -lGLU -ljpeg -lm -lpthread
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<GL/gl.h>
#include<GL/glu.h>
#include<GL/glut.h>


void initgl(int width, int height);
void resize(int width, int height);
void render();
void keydown(unsigned char key, int x, int y);
void specialkey(int key, int x, int y);
void releaseblock();
void moveleft();
void moveright();
void movedown();
void shiftup();
void shiftdown();
void procblocks();
void checkformovedown();
void clearboard();

enum COLOR { BLACK = 0, WHITE, RED, GREEN, BLUE, GRAY };


struct block {

  float r,g,b;
  COLOR color;

  block()    
  {
    color = BLACK;
    r = 0.0f;
    g = 0.0f;
    b = 0.0f;
  }
  
  inline void copyblock(struct block *g_block )
  {
    color = g_block->color;
    r = g_block->r;
    g = g_block->g;
    b = g_block->b;
  }

  inline void clear()
  {
    color = BLACK;
    r = 0.0f;
    g = 0.0f;
    b = 0.0f;
  }

};

struct gameblock : block {

  int x,y;

  gameblock()
  {
    color = BLACK;
    r = 0.0f;
    g = 0.0f;
    b = 0.0f;
    x = 0;
    y = 0;
  }
  
  inline void randcolor()
  {
    switch(rand()%5)
    {
      case 0:
      r = 1.0f;
      g = 0.0f;
      b = 0.0f;
      color = RED;
      break;
      case 1:
      r = 0.0f;
      g = 1.0f;
      b = 0.0f;
      color = GREEN;
      break;      
      case 2:
      r = 0.0f;
      g = 0.0f;
      b = 1.0f;
      color = BLUE;
      break;
      case 3:
      r = 1.0f;
      g = 1.0f;
      b = 1.0f;
      color = WHITE;
      break;
      case 4:
      case 5:
      r = .5f;
      g = .5f;
      b = .5f;
      color = GRAY;
      break;
    }
  }

};

struct block tiles[17][8];
struct gameblock gblock[3];

float rquad = 0.0f, r_quad = 0.0f;
int wnd = 0, i = 0,z = 0, p = 0,score = 0;

int main(int argc, char * argv[])
{
    srand(time(0));
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);
    glutInitWindowSize(640, 480);
    glutInitWindowPosition(0, 0);
    wnd = glutCreateWindow("MasterPiece 3D");
    glutDisplayFunc(&render);
    //glutFullScreen();
    glutIdleFunc(&render);
    glutReshapeFunc(&resize);
    glutKeyboardFunc(&keydown);
    glutSpecialFunc(&specialkey);
    initgl(640, 480);
    releaseblock();
    glutMainLoop();
    return (0);
}

void initgl(int width, int height)
{
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClearDepth(1.0);
  glDepthFunc(GL_LESS);
  glEnable(GL_DEPTH_TEST);
  glShadeModel(GL_SMOOTH);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45.0f,(float)width/(float)height,0.1f,100.0f);
  glMatrixMode(GL_MODELVIEW);
}

void resize(int width, int height)
{
    if (height==0)
    height=1;
  glViewport(0, 0, width, height);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  gluPerspective(45.0f,(float)width/(float)height,0.1f,100.0f);
  glMatrixMode(GL_MODELVIEW);
}

void render()
{
    checkformovedown();
    procblocks();
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glLoadIdentity();

    glTranslatef(-10.0f,15.0f,-45.0f);

    glRotatef(rquad,1.0f,0.0f,0.0f);
    glRotatef(r_quad,0.0f,1.0f,0.0f);

    //draw the board
    for(i = 0; i < 17; i++)
    {

      for(z = 0; z < 8; z++)
      {
        for(p = 0; p < 3; p++)
        {

        // draw the current game block 
        if(gblock[p].x == z && gblock[p].y == i)
        {
            glBegin(GL_QUADS);

            glColor3f(gblock[p].r + .3f, gblock[p].g + .3f, gblock[p].b + .3f);
            glVertex3f( 1.0f, 1.0f,-1.0f);
            glVertex3f(-1.0f, 1.0f,-1.0f);
            glVertex3f(-1.0f, 1.0f, 1.0f);
            glVertex3f( 1.0f, 1.0f, 1.0f);

            glColor3f(gblock[p].r + .3f, gblock[p].g + .3f, gblock[p].b + .3f);
            glVertex3f( 1.0f,-0.5f, 1.0f);
            glVertex3f(-1.0f,-0.5f, 1.0f);
            glVertex3f(-1.0f,-0.5f,-1.0f);
            glVertex3f( 1.0f,-0.5f,-1.0f);

            glColor3f(gblock[p].r,gblock[p].g,gblock[p].b);
            glVertex3f( 1.0f, 1.0f, 1.0f);
            glVertex3f(-1.0f, 1.0f, 1.0f);
            glVertex3f(-1.0f,-.5f, 1.0f);
            glVertex3f( 1.0f,-.5f, 1.0f);

            glColor3f(gblock[p].r + .2f,gblock[p].g + .2f,gblock[p].b + .2f);
            glVertex3f( 1.0f,-.5f,-1.0f);
            glVertex3f(-1.0f,-.5f,-1.0f);
            glVertex3f(-1.0f, 1.0f,-1.0f);
            glVertex3f( 1.0f, 1.0f,-1.0f);

            glColor3f(gblock[p].r + .1f, gblock[p].g + .1f,gblock[p].b + .1f);
            glVertex3f(-1.0f, 1.0f,1.0f);
            glVertex3f(-1.0f, 1.0f,-1.0f);
            glVertex3f(-1.0f, -0.5f,-1.0f);
            glVertex3f(-1.0f, -0.5f, 1.0f);

            glVertex3f(1.0f,1.0f,1.0f);
            glVertex3f(1.0f,1.0f,-1.0f);
            glVertex3f(1.0f,-0.5f,-1.0f);
            glVertex3f(1.0f,-0.5f,1.0f);

            glEnd();
        }

      }
      
        // draw the rest of the blocks
        if(tiles[i][z].color != BLACK)
        {
            glBegin(GL_QUADS);

            glColor3f(tiles[i][z].r + .3f, tiles[i][z].g + .3f, tiles[i][z].b + .3f);
            glVertex3f( 1.0f, 1.0f,-1.0f);
            glVertex3f(-1.0f, 1.0f,-1.0f);
            glVertex3f(-1.0f, 1.0f, 1.0f);
            glVertex3f( 1.0f, 1.0f, 1.0f);

            glColor3f(tiles[i][z].r + .3f, tiles[i][z].g + .3f, tiles[i][z].b + .3f);
            glVertex3f( 1.0f,-0.5f, 1.0f);
            glVertex3f(-1.0f,-0.5f, 1.0f);
            glVertex3f(-1.0f,-0.5f,-1.0f);
            glVertex3f( 1.0f,-0.5f,-1.0f);

            glColor3f(tiles[i][z].r,tiles[i][z].g,tiles[i][z].b);
            glVertex3f( 1.0f, 1.0f, 1.0f);
            glVertex3f(-1.0f, 1.0f, 1.0f);
            glVertex3f(-1.0f,-.5f, 1.0f);
            glVertex3f( 1.0f,-.5f, 1.0f);

            glColor3f(tiles[i][z].r + .2f,tiles[i][z].g + .2f,tiles[i][z].b + .2f);
            glVertex3f( 1.0f,-.5f,-1.0f);
            glVertex3f(-1.0f,-.5f,-1.0f);
            glVertex3f(-1.0f, 1.0f,-1.0f);
            glVertex3f( 1.0f, 1.0f,-1.0f);

            glColor3f(tiles[i][z].r + .1f, tiles[i][z].g + .1f,tiles[i][z].b + .1f);
            glVertex3f(-1.0f, 1.0f,1.0f);
            glVertex3f(-1.0f, 1.0f,-1.0f);
            glVertex3f(-1.0f, -0.5f,-1.0f);
            glVertex3f(-1.0f, -0.5f, 1.0f);

            glVertex3f(1.0f,1.0f,1.0f);
            glVertex3f(1.0f,1.0f,-1.0f);
            glVertex3f(1.0f,-0.5f,-1.0f);
            glVertex3f(1.0f,-0.5f,1.0f);

            glEnd();

          }
          glTranslatef(3.0f,0.0f,0.0f);
       }

      glTranslatef(-24.0,-2.0,0.0f);

    }

    glutSwapBuffers();
  
}

void keydown(unsigned char key, int x, int y)
 {
     switch(key)
     {
     case 27:
     {
 	    glutDestroyWindow(wnd);
 	    exit(0);
 	   }
     break;
     case 'a':
     case 'A':
     shiftup();
     break;
     case 'S':
     case 's':
     shiftdown();
     break;
     }
 }

void specialkey(int key, int x, int y)
{
     switch(key)
     {
     case 104:
	   {
	       rquad += 0.5;
	   }
	   break;
     case 105:
	   {

	       rquad -= 0.5;
	   }
	   break;
     case 106:
	   {
	       r_quad -= 0.5f;
     }
	   break;
     case 107:
	   {
	       r_quad += 0.5f;
	   }
	   break;
     case GLUT_KEY_UP:
     {
       releaseblock();
     }
     break;
     case GLUT_KEY_DOWN:
     {
       movedown();
     }
     break;
     case GLUT_KEY_LEFT:
     {
       moveleft();
     }
     break;
     case GLUT_KEY_RIGHT:
     {
       moveright();
     }
     break;
     }
     
}

void releaseblock()
{
do
{
  gblock[0].randcolor();
  gblock[1].randcolor();
  gblock[2].randcolor();
}
while(gblock[0].color == gblock[1].color && gblock[0].color == gblock[2].color);

  gblock[0].x = 3;
  gblock[0].y = 0;
  gblock[1].x = 3;
  gblock[1].y = 1;
  gblock[2].x = 3;
  gblock[2].y = 2;
  
}

void moveleft()
{
  if(gblock[0].x > 0 && tiles[gblock[0].y][gblock[0].x-1].color == BLACK && tiles[gblock[1].y][gblock[1].x-1].color == BLACK && tiles[gblock[2].y][gblock[2].x-1].color == BLACK)
  {
     gblock[0].x--;
     gblock[1].x--;
     gblock[2].x--;
  }
}

void moveright()
{
  if(gblock[0].x < 7 && tiles[gblock[0].y][gblock[0].x+1].color == BLACK && tiles[gblock[1].y][gblock[1].x+1].color == BLACK && tiles[gblock[2].y][gblock[2].x+1].color == BLACK)
  {
    gblock[0].x++;
    gblock[1].x++;
    gblock[2].x++;
  }
}

void movedown()
{
  if(gblock[2].y < 16)
  {
    if(tiles[gblock[2].y+1][gblock[2].x].color != BLACK)
    {
      tiles[gblock[2].y][gblock[2].x].copyblock(&gblock[2]);
      tiles[gblock[1].y][gblock[1].x].copyblock(&gblock[1]);
      tiles[gblock[0].y][gblock[0].x].copyblock(&gblock[0]);
      if(gblock[2].y < 3)
      {
        clearboard();
        printf("game over\nyour score: %i\n", score);
      }
      else
      releaseblock();
      
    }  
    else
    {
      gblock[0].y++;
      gblock[1].y++;
      gblock[2].y++;
    }
  }
  else
  {
    tiles[gblock[2].y][gblock[2].x].copyblock(&gblock[2]);
    tiles[gblock[1].y][gblock[1].x].copyblock(&gblock[1]);
    tiles[gblock[0].y][gblock[0].x].copyblock(&gblock[0]);
    releaseblock();
  }
}

void shiftup()
{
  struct gameblock g_block;
  g_block = gblock[0];
  gblock[0].copyblock(&gblock[1]);
  gblock[1].copyblock(&gblock[2]);
  gblock[2].copyblock(&g_block);
}

void shiftdown()
{
  struct gameblock g_block;
  g_block = gblock[0];
  gblock[0].copyblock(&gblock[2]);
  gblock[2].copyblock(&gblock[1]);
  gblock[1].copyblock(&g_block);
}

void procblocks()
{

  for(i = 0; i < 17; i++)
  {
    for(z = 0; z < 8; z++)
    {
      COLOR cur_color = tiles[i][z].color;

      if(cur_color != BLACK)
      {
        if(z+2 < 8)
        {
          if(tiles[i][z+1].color == cur_color && tiles[i][z+2].color == cur_color)
          {
            tiles[i][z].clear();
            tiles[i][z+1].clear();
            tiles[i][z+2].clear();
            score += 5;
            return;
          }
        }

        if(z+3 < 8)
        {
          if(tiles[i][z+1].color == cur_color && tiles[i][z+2].color == cur_color && tiles[i][z+3].color == cur_color)
          {
            tiles[i][z].clear();
            tiles[i][z+1].clear();
            tiles[i][z+2].clear();
            tiles[i][z+3].clear();
            score += 7;
            return;
          }
       }

        if(i+2 < 17)
        {
          if(tiles[i+1][z].color == cur_color && tiles[i+2][z].color == cur_color)
          {
            tiles[i][z].clear();
            tiles[i+1][z].clear();
            tiles[i+2][z].clear();
            score += 5;
            return;
          }
        }
        
        if(i+3 < 17)
        {
          if(tiles[i+1][z].color == cur_color && tiles[i+2][z].color == cur_color)
          {
            tiles[i][z].clear();
            tiles[i+1][z].clear();
            tiles[i+2][z].clear();
            tiles[i+3][z].clear();
            score += 7;
            return;
          }
        }
      }
      
    }
  }

  for(z = 0; z < 8; z++)
  {
    for(i = 0; i < 17; i++)
    {
      COLOR cur_color = tiles[i][z].color;
      if(cur_color != BLACK)
      {
        if(i+2 < 17 && z+2 < 8)
        {
          
          if(tiles[i+1][z+1].color == cur_color && tiles[i+2][z+2].color == cur_color)
          {
            tiles[i][z].clear();
            tiles[i+1][z+1].clear();
            tiles[i+2][z+2].clear();
            score += 10;
            return;
          }

        }
        if(i-2 >= 0 && z-2 >= 0)
        {
          if(tiles[i-1][z-1].color == cur_color && tiles[i-2][z-2].color == cur_color)
          {
            tiles[i][z].clear();
            tiles[i-1][z-1].clear();
            tiles[i-2][z-2].clear();
            score += 10;
            return;
          }
        }

        if(i-2 >= 0)
        {
          if(tiles[i-1][z+1].color == cur_color && tiles[i-2][z+2].color == cur_color)
          {
            tiles[i][z].clear();
            tiles[i-1][z+1].clear();
            tiles[i-2][z+2].clear();
            score += 10;
            return;
          }
        }

        if(z-2 >= 0)
        {
          if(tiles[i+1][z-1].color == cur_color && tiles[i+2][z-2].color == cur_color)
          {
            tiles[i][z].clear();
            tiles[i+1][z-1].clear();
            tiles[i+2][z-2].clear();
            score += 10;
            return;            
          }
        }
            
      }
    }
  }
  
  for(z = 0; z < 8; z++)
  {
    for(i = 0; i < 16; i++)
    {
      if(tiles[i][z].color != BLACK && tiles[i+1][z].color == BLACK)
      {
        tiles[i+1][z] = tiles[i][z];
        tiles[i][z].clear();
      }
    }
  }
  
   
}

void checkformovedown()
{
  time_t t = time(0);
  static time_t st_t = time(0);
  if(t > st_t)
  {
    movedown();
    st_t = t;
  }
}

void clearboard()
{
  for(i = 0; i < 17; i++)
  {
    for(z = 0; z < 8; z++)
    {
      tiles[i][z].clear();
    }
  }
}

