/ Greg Kaiser
//
// CS290 with Prof. Francis
// 4D Tetris
//
// Main.C
// Last modified: April 25, 1996
//
// (C) 1996 Board of Trustees University of Illinois
 

#include <sys/time.h>
#include <device.h>
#include <gl.h>
#include <iostream.h>
#include <strings.h>
#include <math.h>
#include "Board.h"
#include "Shared.h"
#include "GenPiece.h"
#include "Pieces.h"
#include "Rotor.h"

#define SOAK(K)  while(getbutton(K))

int *DIM;
float *LEN;
Board *board;
float *botcorner;
int SHADEUNDER;

void InitGlobals(int *dims)
{
  DIM = new int[4];
  for (int i = 0; i < 4; i++)
    DIM[i] = dims[i];
  LEN = new float[4];
  LEN[X] = LEN[Y] = LEN[Z] = LEN[W] = 4.5 / (float)(DIM[W]);
  botcorner = new float[4];
  for (i = 0; i < 4; i++)
    botcorner[i] = -2.0 + ((4.0 - LEN[i] * DIM[i]) / 2);
  board = new Board();
}

GenPiece *NewPiece(int numpieces, int *mypieces)
{
  int piecenum = mypieces[(random() % numpieces)];
  //cout << piecenum << endl;

  GenPiece *ans;
  switch (piecenum) {
  case 1:
    ans = new CubePiece();
    break;

  case 2:
    ans = new LPiece();
    break;

  case 3:
    ans = new SPiece();
    break;

  case 4:
    ans = new StraightPiece();
    break;

  case 5:
    ans = new TPiece();
    break;

  case 6:
    ans = new CornerPiece();
    break;

  case 7:
    ans = new TwistyPiece();
    break;

  case 8:
    ans = new ZPiece();
    break;

  case 9:
    ans =  new ThreeLinePiece();
    break;

  case 10:
    ans = new TriPiece();
    break;

  case 11:
    ans = new TwoPiece();
    break;

  case 12:
    ans = new SimplePiece();
    break;
  }

  return ans;
}
 

void CleanUpGlobals()
{
  delete DIM;
  delete LEN;
  delete board;
  delete botcorner;
}

int MyQuad(int getx, int gety) {
  long *sx, *sy, *ox, *oy;
  sx = new long;
  sy = new long;
  ox = new long;
  oy = new long;
  getsize(sx, sy);
  getorigin(ox, oy);
  int ans;

  if (getx <= ((*ox) + (*sx) / 2))
    ans = ((gety <= ((*oy) + (*sy) / 2)) ? 2 : 0);
  else
    ans = ((gety <= ((*oy) + (*sy) / 2)) ? 3 : 1);

  delete sx;
  delete sy;
  delete ox;
  delete oy;
  return ans;
}

int StrToInt(char *changeme) {
  int ans = (int)(changeme[0]) - 48;
  for (int i = 1; changeme[i] != '\0'; i++)
    ans = 10*ans + (int)(changeme[i]) - 48;
  return ans;
}
void main(int argc, char **argv)
{
  cout << "\n\nHyperTetris\n    by\nGreg Kaiser\n" << endl;
  cout << "(C) 1996 Board of Trustees University of Illinois\n" << endl;
 
 
 
 

//instructions (mbm4/18/01)

  cout << "Four Quadrants of Game Screen:" << endl;
  cout << "Top-Left (w,x,y)       Top-Right (x,y,z)" << endl;
  cout << "Bottom-Left (z,w,x)    Bottom-Right (y,z,w)\n" << endl;

  cout << "Controls:   (number keys on numerical keypad)\n" << endl;
  cout << "Translation (moving pieces back and forth)" << endl;
  cout << "dimention  +key   -key" << endl;
  cout << "                            7 _   ^8  _ 9 " << endl;
  cout << "  x       left 4  right 6    |`  '|`  '|" << endl;
  cout << "                               `  |  '  " << endl;
  cout << "  y       up   8  down  2       ` | '" << endl;
  cout << "                             x   `|'    " << endl;
  cout << "  z       in   9  out   1   4-----*---->6" << endl;
  cout << "                                 '|`    " << endl;
  cout << "  w       ana  7  cata  3       ' | `" << endl;
  cout << "                               '  |  `  " << endl;
  cout << "                             z'   |   `w" << endl;
  cout << "                            1'   y|2   `3\n" << endl;

  cout << "Rotation:  to rotate a piece, press ALT and two number keys as above" << endl;
  cout << "For example: ALT, 4,and 8 pressed at the same time will rotate" << endl;
  cout << "The piece between the -x and +y directions: (a straight piece along" << endl;
  cout << "along the x direction becomes a straight line along the y direction)\n" << endl;
  cout << "Remember, not all the dimentions exist in each image.  All four" << endl;
  cout << "images may not be visably affected by a single rotation.\n" << endl;

  cout << "Spacebar drops the current piece (moves to bottom of w dimention)" << endl;
  cout << "The F key stops `gravity'... the piece will not fall (down w dimention), but " << endl;
  cout << "it can still by moved (i.e. translated or rotated)\n" << endl;

  cout << "see the README for more detailed and obscure commands." << endl;

//end of m. murphey's instructions add in
 
 
 

  keepaspect(1,1);
  prefposition(300, 1285, 0, 985);
  winopen("4-D Tetris"); // make sure you open the window BEFORE initializing
                         // any data structures.  Otherwise, you'll dump core
  winconstraints();  // should allow window to be resized

  timeval *randomnumberssuck = new timeval;
  gettimeofday(randomnumberssuck);
  srandom(randomnumberssuck->tv_sec);
  delete randomnumberssuck;
  qdevice(PAD1);
  qdevice(PAD2);
  qdevice(PAD3);
  qdevice(PAD4);
  qdevice(PAD6);
  qdevice(PAD7);
  qdevice(PAD8);
  qdevice(PAD9);
  qdevice(SPACEKEY);
  qdevice(F1KEY);
  qdevice(F2KEY);
  qdevice(F3KEY);
  qdevice(F4KEY);
  qdevice(F5KEY);
  qdevice(F6KEY);
  qdevice(F7KEY);
  qdevice(F8KEY);
  qdevice(F9KEY);
  qdevice(F10KEY);
  qdevice(F11KEY);
  qdevice(F12KEY);

  int gameon = 1;
  Rotor **quad = new Rotor*[4];
  for (int i = 0; i < 4; i++)
    quad[i] = new Rotor();

  int AUTOFALL = 1;  /* gkf */
  int *dims = new int[4];
  dims[X] = dims[Y] = dims[Z] = 4;
  dims[W] = 10;
  int ONE_TURN_AT_A_TIME = 1;
  int fallsec =  4;
  int fallusec = 0;
  int numpieces;
  int *mypieces = NULL;
  SHADEUNDER = 0;

  i = 1;

  while (i < argc) {
    if (!(strcmp(argv[i], "-NOFALL")))
      AUTOFALL = 0;

    else if (!(strcmp(argv[i], "-DIM")))
      for (int j = 0; j < 4; j++) {
        i++;
        dims[j] = StrToInt(argv[i]);
      }
 

    else if (!(strcmp(argv[i], "-MULTITURN")))
      ONE_TURN_AT_A_TIME = 0;

    else if (!(strcmp(argv[i], "-FALLTIME"))) {
      i++;
      fallsec = StrToInt(argv[i++]);
      fallusec = StrToInt(argv[i]);

    } else if (!(strcmp(argv[i], "-MYPIECES"))) {
      i++;
      numpieces = StrToInt(argv[i]);
      mypieces = new int[numpieces];
      for (int j = 0; j < numpieces; j++) {
        i++;
        mypieces[j] = StrToInt(argv[i]);
      }

    } else if (!(strcmp(argv[i], "-SHADEUNDER")))
      SHADEUNDER = 1;

    else {
      cout << endl;
      cout << "The command line argument " << argv[i] << " isn't recognized.";
      cout << endl << "Please see the README file for acceptable arguments.";
      cout << endl << "Thank you.  Have a nice day!" << endl;
    }

    i++;
  }

  InitGlobals(dims);

  if (mypieces == NULL) {
    numpieces = 11;
    mypieces = new int[numpieces];
    for (int i = 0; i < numpieces; i++) {
      mypieces[i] = i + ((i < 7) ? 1 : 2);
    }
  }

  int numremoved = 0;
  timeval *oldtime, *newtime, *temptime, *waiting;

  if (AUTOFALL) {
    oldtime = new timeval();
    newtime = new timeval();
    temptime = new timeval();
    waiting = new timeval();

    waiting->tv_sec = fallsec;
    waiting->tv_usec = fallusec;

  } else
    oldtime = newtime = temptime = waiting = NULL;

  GenPiece *dude = NewPiece(numpieces, mypieces);
  //GenPiece *dude = new CubePiece();
  //GenPiece *dude = new LPiece();
  //GenPiece *dude = new SPiece();
 

  //GenPiece *dude = new StraightPiece();
  //GenPiece *dude = new TPiece();
  //GenPiece *dude = new CornerPiece();
  //GenPiece *dude = new TwistyPiece();
  //GenPiece *dude = new ZPiece();
  //GenPiece *dude = new ThreeLinePiece();
  //GenPiece *dude = new TriPiece();
  //GenPiece *dude = new TwoPiece();
  //GenPiece *dude = new SimplePiece();

  zbuffer(1); doublebuffer(); RGBmode();  gconfig();
  qdevice(MOUSEX); qdevice(MOUSEY);

  if (AUTOFALL) {
    gettimeofday(oldtime);
    newtime->tv_sec = oldtime->tv_sec + waiting->tv_sec;
    newtime->tv_usec = oldtime->tv_usec + waiting->tv_usec;
  }

  while (gameon && !getbutton(ESCKEY)) {
    ortho(-5.01, 5.01, -5.01, 5.01, -5.01, 5.01);
 
 

  //mbm  here's Prof. Francis and mine "gravity toggle"  {  /* gkf */
     if(getbutton(FKEY))AUTOFALL = 1-AUTOFALL; SOAK(FKEY);
    }
//it ends here
 
 

    // if there is to be a rotation
    if (gameon == 1)
      if (getbutton(LEFTALTKEY) || getbutton(RIGHTALTKEY)) {
        if (getbutton(PAD6)) {
          if (getbutton(PAD8)) {
            SOAK(PAD6);
            SOAK(PAD8);
            dude->Rotate(X, Y, 1);
          } else if (getbutton(PAD2)) {
            SOAK(PAD6);
            SOAK(PAD2);
            dude->Rotate(X, Y, -1);
          } else if (getbutton(PAD9)) {
            SOAK(PAD6);
            SOAK(PAD9);
            dude->Rotate(X, Z, 1);
          } else if (getbutton(PAD1)) {
            SOAK(PAD6);
            SOAK(PAD1);
            dude->Rotate(X, Z, -1);
          } else if (getbutton(PAD7)) {
            SOAK(PAD6);
            SOAK(PAD7);
            dude->Rotate(X, W, 1);
          } else if (getbutton(PAD3)) {
            SOAK(PAD6);
            SOAK(PAD3);
            dude->Rotate(X, W, -1);
          }
        } else if (getbutton(PAD4)) {
          if (getbutton(PAD8)) {
            SOAK(PAD4);
            SOAK(PAD8);
            dude->Rotate(X, Y, -1);
          } else if (getbutton(PAD2)) {
            SOAK(PAD4);

            SOAK(PAD2);
            dude->Rotate(X, Y, 1);
          } else if (getbutton(PAD9)) {
            SOAK(PAD4);
            SOAK(PAD9);
            dude->Rotate(X, Z, -1);
          } else if (getbutton(PAD1)) {
            SOAK(PAD4);
            SOAK(PAD1);
            dude->Rotate(X, Z, 1);
          } else if (getbutton(PAD7)) {
            SOAK(PAD4);
            SOAK(PAD7);
            dude->Rotate(X, W, -1);
          } else if (getbutton(PAD3)) {
            SOAK(PAD4);
            SOAK(PAD3);
          dude->Rotate(X, W, 1);
          }
        } else if (getbutton(PAD8)) {
          if (getbutton(PAD9)) {
            SOAK(PAD8);
            SOAK(PAD9);
            dude->Rotate(Y, Z, 1);
          } else if (getbutton(PAD1)) {
            SOAK(PAD8);
            SOAK(PAD1);
            dude->Rotate(Y, Z, -1);
          } else if (getbutton(PAD7)) {
            SOAK(PAD8);
            SOAK(PAD7);
            dude->Rotate(Y, W, 1);
          } else if (getbutton(PAD3)) {
            SOAK(PAD8);
            SOAK(PAD3);
            dude->Rotate(Y, W, -1);
          }
        } else if (getbutton(PAD2)) {
          if (getbutton(PAD9)) {
            SOAK(PAD2);
            SOAK(PAD9);
            dude->Rotate(Y, Z, -1);
          } else if (getbutton(PAD1)) {
            SOAK(PAD2);
            SOAK(PAD1);
            dude->Rotate(Y, Z, 1);
          } else if (getbutton(PAD7)) {
            SOAK(PAD2);
            SOAK(PAD7);
            dude->Rotate(Y, W, -1);
          } else if (getbutton(PAD3)) {
            SOAK(PAD2);
            SOAK(PAD3);
            dude->Rotate(Y, W, 1);
          }
        } else if (getbutton(PAD9)) {
          if (getbutton(PAD7)) {
            SOAK(PAD9);
            SOAK(PAD7);
            dude->Rotate(Z, W, 1);
          } else if (getbutton(PAD3)) {
            SOAK(PAD9);
            SOAK(PAD3);
            dude->Rotate(Z, W, -1);
          }
        } else if (getbutton(PAD1)) {
          if (getbutton(PAD7)) {
            SOAK(PAD1);
            SOAK(PAD7);
            dude->Rotate(Z, W, -1);
          } else if (getbutton(PAD3)) {
            SOAK(PAD1);
            SOAK(PAD3);
            dude->Rotate(Z, W, 1);
          }
        }
      } // the ALT keys

      // otherwise look for a translation
      else if (getbutton(PAD6)) {
        SOAK(PAD6);
        dude->Translate(X, 1);
      } else if (getbutton(PAD4)) {
        SOAK(PAD4);
        dude->Translate(X, -1);
      } else if (getbutton(PAD8)) {
        SOAK(PAD8);
        dude->Translate(Y, 1);
      } else if (getbutton(PAD2)) {
        SOAK(PAD2);
        dude->Translate(Y, -1);
      } else if (getbutton(PAD9)) {
        SOAK(PAD9);
        dude->Translate(Z, 1);
      } else if (getbutton(PAD1)) {
        SOAK(PAD1);
        dude->Translate(Z, -1);
      } else if (getbutton(PAD7)) {
        SOAK(PAD7);
        dude->Translate(W, 1);
      } else if (getbutton(PAD3)) {
        SOAK(PAD3);
        dude->Translate(W, -1);
      }

      // drop the piece
      else if (getbutton(SPACEKEY)) {
        SOAK(SPACEKEY);
        dude->Drop();
        gameon = dude->DonePiece();
        delete dude;
        dude = NewPiece(numpieces, mypieces);
      }

      // take a look at the different pieces
      else if (getbutton(F1KEY)) {
        SOAK(F1KEY);
        dude->Drop();
        gameon = dude->DonePiece();
        delete dude;

        dude = new CubePiece();
      } else if (getbutton(F2KEY)) {
        SOAK(F2KEY);
        dude->Drop();
        gameon = dude->DonePiece();
        delete dude;
        dude = new LPiece();
      } else if (getbutton(F3KEY)) {
        SOAK(F3KEY);
        dude->Drop();
        gameon = dude->DonePiece();
        delete dude;
        dude = new SPiece();
      } else if (getbutton(F4KEY)) {
        SOAK(F4KEY);
        dude->Drop();
        gameon = dude->DonePiece();
        delete dude;
        dude = new StraightPiece();
      } else if (getbutton(F5KEY)) {
        SOAK(F5KEY);
        dude->Drop();
        gameon = dude->DonePiece();
        delete dude;
        dude = new TPiece();
      } else if (getbutton(F6KEY)) {
        SOAK(F6KEY);
        dude->Drop();
        gameon = dude->DonePiece();
        delete dude;
        dude = new CornerPiece();
      } else if (getbutton(F7KEY)) {
        SOAK(F7KEY);
        dude->Drop();
        gameon = dude->DonePiece();
        delete dude;
        dude = new TwistyPiece();
      } else if (getbutton(F8KEY)) {
        SOAK(F8KEY);
        dude->Drop();
        gameon = dude->DonePiece();
        delete dude;
        dude = new ZPiece();
      } else if (getbutton(F9KEY)) {
        SOAK(F9KEY);
        dude->Drop();
        gameon = dude->DonePiece();
        delete dude;
        dude = new ThreeLinePiece();
      } else if (getbutton(F10KEY)) {
        SOAK(F10KEY);
        dude->Drop();
        gameon = dude->DonePiece();
        delete dude;
        dude = new TriPiece();
      } else if (getbutton(F11KEY)) {
        SOAK(F11KEY);
        dude->Drop();
        gameon = dude->DonePiece();
        delete dude;
 

        dude = new TwoPiece();
      } else if (getbutton(F12KEY)) {
        SOAK(F12KEY);
        dude->Drop();
        gameon = dude->DonePiece();
        delete dude;
        dude = new SimplePiece();
      }

    if (AUTOFALL && (gameon == 1)) {
      gettimeofday(temptime);
      if ((temptime->tv_sec > newtime->tv_sec) ||
          ((temptime->tv_sec == newtime->tv_sec) &&
           (temptime->tv_usec >= newtime->tv_usec))) {
        newtime->tv_sec = temptime->tv_sec + waiting->tv_sec;
        newtime->tv_usec = temptime->tv_usec + waiting->tv_usec;
        if (!(dude->Translate(W, -1))) {
          gameon = dude->DonePiece();
          delete dude;
          //dude = new CubePiece();
          dude = NewPiece(numpieces, mypieces);
        }
      }
    }

    // pause/unpause the game
    if (getbutton(MOUSE2)) {
      SOAK(MOUSE2);
      gameon = -gameon;
    }

    if (getbutton(LEFTCTRLKEY) || getbutton(RIGHTCTRLKEY))
      if (getbutton(MOUSE1)) {
        SOAK(MOUSE1);
        if (ONE_TURN_AT_A_TIME)
          for (int i = 0; i < 4; i++)
            quad[i]->Deactivate();

        quad[MyQuad(getvaluator(MOUSEX), getvaluator(MOUSEY))]->Activate();

      } else if (getbutton(MOUSE3)) {
        SOAK(MOUSE3);
        for (int i = 0; i < 4; i++)
          quad[i]->Deactivate();
      }

    cpack(0); clear(); zclear();
    reshapeviewport();

    if (gameon == 1) {
      numremoved += board->CheckBoard();

      if (AUTOFALL && (numremoved >= 2)) {
        int rem = waiting->tv_sec % 8;
        rem *= 7;
        rem %= 8;
        rem = (int)((float)(rem)*5000000.0/8.0);
        waiting->tv_sec = (int)((float)(waiting->tv_sec)*7.0/8.0);
        waiting->tv_usec = (int)((float)(waiting->tv_usec)*7.0/8.0) + rem;
 

        numremoved = 0;
      }
    }

    // put the x, y, z axes in the upper right hand corner (quad 1)
    pushmatrix();
    translate(2.5, 2.5, 0.0);
    quad[1]->Rotate();
    dude->Drawit(7);
    board->Drawit(7);
    popmatrix();

    // put the w, x, y axes in the upper left hand corner (quad 0)
    pushmatrix();
    translate(-2.5, 2.5, 0.0);
    quad[0]->Rotate();
    dude->Drawit(11);
    board->Drawit(11);
    popmatrix();

    // put the z, w, x axes in the lower left corner (quad 2)
    pushmatrix();
    translate(-2.5, -2.5, 0.0);
    quad[2]->Rotate();
    dude->Drawit(13);
    board->Drawit(13);
    popmatrix();

    // put the y, z, w axes in the lower right corner (quad 3)
    pushmatrix();
    translate(2.5, -2.5, 0.0);
    quad[3]->Rotate();
    dude->Drawit(14);
    board->Drawit(14);
    popmatrix();

    swapbuffers();
  }

  if (gameon == 0)
    cout << "Game Over, Man!" << endl;
  else
    cout << "Thanks for playing" << endl;

  delete dude;
  if (AUTOFALL) {
    delete oldtime;
    delete newtime;
    delete temptime;
    delete waiting;
  }
}