#include <stdlib.h>
#include <GL/glut.h>
#include <math.h>

#define STEPCYCLE 50   /* 手足のひと振りに要する時のフレーム数 */
#define WALKCYCLE 250  /* ロボットの移動に要するフレーム数 */
#define KICKSPEED 600  /* キックの速度に関するパラメータ */
#define M_PI 3.14

struct robotangle{
	double ll1; /* ロボットの左足の股関節の角度 */
	double ll2; /* ロボットの左足の膝関節の角度 */
	double rl1; /* ロボットの右足の股関節の角度 */
	double rl2; /* ロボットの右足の膝関節の角度 */
	double la1; /* ロボットの左腕の肩関節の角度 */
	double la2; /* ロボットの左腕の肘関節の角度 */
	double ra1; /* ロボットの右腕の肩関節の角度 */
	double ra2; /* ロボットの右腕の肘関節の角度 */
};

/* 直方体を描く */
void myBox(double x, double y, double z)
{
  GLdouble hx = x * 0.5, hz = z * 0.5;
  static GLfloat flesh[] = { 0.945, 0.733, 0.576, 1.0 };  /* 肌色の定義 */
  int i, j;

  /* 面を構成する頂点 */
  GLdouble vertex[][3] = {
    { -hx,   -y, -hz },
    {  hx,   -y, -hz },
    {  hx,  0.0, -hz },
    { -hx,  0.0, -hz },
    { -hx,   -y,  hz },
    {  hx,   -y,  hz },
    {  hx,  0.0,  hz },
    { -hx,  0.0,  hz }
  };

  /* 面の構成 */
  static int face[][4] = {
    { 0, 1, 2, 3 },
    { 1, 5, 6, 2 },
    { 5, 4, 7, 6 },
    { 4, 0, 3, 7 },
    { 4, 5, 1, 0 },
    { 3, 2, 6, 7 }
  };

  /* 各面の単位法線ベクトル */
  static GLdouble normal[][3] = {
    { 0.0, 0.0,-1.0 },
    { 1.0, 0.0, 0.0 },
    { 0.0, 0.0, 1.0 },
    {-1.0, 0.0, 0.0 },
    { 0.0,-1.0, 0.0 },
    { 0.0, 1.0, 0.0 }
  };

  /* 材質を設定する */
  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, flesh);

  /* 面の描画 */
  glBegin(GL_QUADS);
  for (j = 0; j < 5; j++) {
    glNormal3dv(normal[j]);
    for (i = 3; i >= 0;i--) {
      glVertex3dv(vertex[face[j][i]]);
    }
  }
  glEnd();
}

/* 腕/足 */
void armleg(double girth, double length, double r1, double r2)
{
  glRotated(r1, 1.0, 0.0, 0.0);
  myBox(girth, length, girth);
  glTranslated(0.0, -0.05 - length, 0.0);
  glRotated(r2, 1.0, 0.0, 0.0);
  myBox(girth, length, girth);
}

/* 地面を描く */
void myGround()
{
  static GLfloat ground[4] = {0.1, 1.0, 0.0, 1.0 };

  glBegin(GL_QUADS);
  glNormal3d(0.0, 1.0, 0.0);
  glMaterialfv(GL_FRONT, GL_DIFFUSE, ground);
  glVertex3f(-10, -1.8, -10);
  glVertex3f(-10, -1.8,  10);
  glVertex3f( 5, -1.8,  10);
  glVertex3f( 5, -1.8, -10);
  glEnd();
}

/* ゴールポストを描く */
void mypost()
{
  /* ゴールポストの各頂点  */
  static GLdouble vertex[][3] = {
    { 4.0, -1.7, -5.0 }, /* A */
    { 4.0, -1.7, 5.0 },  /* B */
    { 4.0, 1.0, 5.0 },   /* C */
    { 4.0, 1.0, -5.0 },  /* D */
    { 2.0, -1.7, -5.0 }, /* E */
    { 2.0, -1.7, 5.0 },  /* F */
    { 2.0, 1.0, 5.0 },   /* G */
    { 2.0, 1.0, -5.0 }   /* H */
  };

  static int edge[][2] = {
    { 0, 1 }, /* ア (A-B) */
    { 1, 2 }, /* イ (B-C) */
    { 2, 3 }, /* ウ (C-D) */
    { 3, 0 }, /* エ (D-A) */
    { 5, 6 }, /* カ (F-G) */
    { 6, 7 }, /* キ (G-H) */
    { 7, 4 }, /* ク (H-E) */
    { 0, 4 }, /* ケ (A-E) */
    { 1, 5 }, /* コ (B-F) */
    { 2, 6 }, /* サ (C-G) */
    { 3, 7 }  /* シ (D-H) */
  };
  
  static GLfloat black[] = { 0.0, 0.0, 0.0, 1.0 };
  static GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };
  int i,j,k;
  
  glLineWidth(5);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, white);	/* 材質を設定する */

  glBegin(GL_LINES);
  for (i = 0; i < 11; ++i) {
    glVertex3dv(vertex[edge[i][0]]);
    glVertex3dv(vertex[edge[i][1]]);
  }
  glEnd();
  
  glMaterialfv(GL_FRONT, GL_DIFFUSE, black);		/* 材質を設定する */
  glLineWidth(1.0);

  /* ゴールネットを描く  */
  glBegin(GL_LINES);
  for(j=0;j<=10;j++){
    for(k=0;k<=10;k++){
      glVertex3f(2.0+0.2*j, -1.7, 5.0);
      glVertex3f(2.0+0.2*j, 1.0, 5.0);
      glVertex3f(2.0, -1.7+0.27*k, 5.0);
      glVertex3f(4.0, -1.7+0.27*k, 5.0);
      
      glVertex3f(2.0+0.2*j, -1.7, -5.0);
      glVertex3f(2.0+0.2*j, 1.0, -5.0);
      glVertex3f(2.0, -1.7+0.27*k, -5.0);
      glVertex3f(4.0, -1.7+0.27*k,- 5.0);
      
      glVertex3f(4.0, -1.7, 5.0-1.0*j);
      glVertex3f(4.0, 1.0, 5.0-1.0*j);
      glVertex3f(4.0, -1.7+0.27*k, -5.0);
      glVertex3f(4.0, -1.7+0.27*k, 5.0);
      
      glVertex3f(2.0, 1.0, 5.0-1.0*j);
      glVertex3f(4.0, 1.0, 5.0-1.0*j);
      glVertex3f(2.0+0.2*j, 1.0, 5.0);
      glVertex3f(2.0+0.2*j, 1.0, -5.0);    
    }
  }
  glEnd();
}

/* 部位ごとに座標変換 */
void trans(struct robotangle *angle){
  
  /* 頭 */
  myBox(0.20, 0.25, 0.22);
  
  /* 胴 */
  glTranslated(0.0, -0.3, 0.0);
  myBox(0.4, 0.6, 0.3);
  
  /* 左足 */
  glPushMatrix();
  glTranslated(0.1, -0.65, 0.0);
  armleg(0.2, 0.4, angle->ll1, angle->ll2);
  glPopMatrix();
  
  /* 右足 */
  glPushMatrix();
  glTranslated(-0.1, -0.65, 0.0);
  armleg(0.2, 0.4, angle->rl1, angle->rl2);
  glPopMatrix();

  /* 左腕 */
  glPushMatrix();
  glTranslated(0.28, 0.0, 0.0);
  armleg(0.16, 0.4, angle->la1, angle->la2);
  glPopMatrix();

  /* 右腕 */
  glPushMatrix();
  glTranslated(-0.28, 0.0, 0.0);
  armleg(0.16, 0.4, angle->ra1, angle->ra2);
  glPopMatrix();

}

/* 画面表示 */
void display(void)
{
  static GLfloat lightpos[] = { 3.0, 4.0, 1.0, 0.0 }; /* 光源の位置 */
  static int frame = 0,i=0,state=0;
  static double bx=0.0;
  static GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };
  static struct robotangle angle;
  static GLUquadricObj *sphere; 		      /* オブジェクトポインタを準備 */
  sphere = gluNewQuadric();			      /* オブジェクトを生成 */

  /* STEPCYCLE に指定した枚数のフレームを描画する間に 0→1 に変化  */
  double t;
  t = (frame % STEPCYCLE) / (double)STEPCYCLE;

  /* WALKCYCLE に指定した枚数のフレームを描画する間に 0→1 に変化  */
  double s;
  s = (frame % WALKCYCLE) / (double)WALKCYCLE;

  double px = -10+20*s, pz = 0.0;      /* ロボットの位置 */

  if(px >= -2.5 && state == 0){
	  t = 0.8;
	  state = 1;
  }

  double r = 90.0;		               /* ロボットの向き */
  double h = 0.01*cos((360*t/360)*2*M_PI*2);   /* ロボットの高さ */

  angle.ll1 = 30*sin((360*t/360)*2*M_PI);      /* ロボットの左足の股関節の角度 */
  angle.ll2 = 30-30*cos((360*t/360)*2*M_PI);   /* ロボットの左足の膝関節の角度 */
  angle.rl1 = 30*cos(((360*t+60)/360)*2*M_PI); /* ロボットの右足の股関節の角度 */
  angle.rl2 = 30-30*sin(((360*t+60)/360)*2*M_PI); /* ロボットの右足の膝関節の角度 */
  angle.la1 = 30*cos(((360*t+60)/360)*2*M_PI);    /* ロボットの左腕の肩関節の角度 */
  angle.la2 = -30-30*sin(((360*t+60)/360)*2*M_PI);   /* ロボットの左腕の肘関節の角度 */
  angle.ra1 = 30*sin((360*t/360)*2*M_PI);      /* ロボットの右腕の肩関節の角度 */
  angle.ra2 = -30-30*cos((360*t/360)*2*M_PI);     /* ロボットの右腕の肘関節の角度 */

  if(state != 1)
      ++frame;		/* フレーム数(画面表示を行った回数)をカウントする */

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* 画面の塗りつぶし・Zバッファの設定 */
  glLoadIdentity();		       	       /* モデルビュー変換行列の初期化 */
  glLightfv(GL_LIGHT0, GL_POSITION, lightpos); /* 光源の位置を設定 */
  glTranslated(0.0, 0.0, -20.0);	                 /* 視点の移動(物体の方を奥に移す)*/
  myGround();				      /* 地面 */
  mypost();		         		      /* ゴールポスト  */
  
  //オブジェクトの描画タイプを設定
  gluQuadricDrawStyle(sphere, GLU_FILL);
  
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, white);	/* 材質を設定する */

  if(state == 0)					/* ドリブル */
    glTranslated(px+0.45,-1.7,0);
  else if(state == 1)				/* ボールを蹴る動作中 */
    glTranslated(-2.5+0.45,-1.7,0);
  else if(state == 2){				/* 蹴った後の動作 */
	  if(bx==0.)
		  bx = px+0.45;
	  glTranslated(bx,bx/4-1.0,0);
	  bx += 0.2;	
  }

  gluSphere(sphere, 0.1,10.0, 10.0);			/* 球を描画 半径0.1,緯経それぞれ10.0分割 */
  
  glLoadIdentity();			         /* モデルビュー変換行列の初期化 */
  glTranslated(0.0, 0.0, -20.0);
  
  /* ロボットの位置と方向 */
  if(state == 0)
    glTranslated(px, h, pz);
  else
    glTranslated(-2.5, 0.0, 0.0);

  glRotated(r, 0.0, 1.0, 0.0);			     /* ロボットの向きの変更 */
  
  /* ボールを蹴る動作以降の右足以外の角度 */
  if(state != 0){
    angle.ll1=-45.;
    angle.ll2=45.;
    angle.la1=0.;
    angle.la2=0.;
    angle.ra1=0.;
    angle.ra2=0.;
    if(state == 2){
      angle.rl1=-90;
      angle.rl2=0;
    }
  }
  
  /* ボールを蹴る動作 */
  if(state == 1){
    i++;
    angle.rl1=45*sin(((double)(0.01*KICKSPEED)*i/360)*2*M_PI);
    angle.rl2=45-45*cos(((double)(0.01*KICKSPEED)*i/360)*2*M_PI);
    if(i==(int)(22000/KICKSPEED)){ 
      state = 2;
      i=0;
    }
  }
  
  /* ゴールネットに突き刺さった場合 */
  if(bx >= 4.0){
      /* パラメータの初期化 */
      state = 0;
      frame = 0;
      bx = 0.0;
      }	
      
      trans(&angle);		/* ロボットの手足の幾何変換 */
      
      glutSwapBuffers();
  
      }

      void resize(int w, int h)
      {
      /* ウィンドウ全体をビューポートにする */
      glViewport(0, 0, w, h);
      
      /* 透視変換行列の指定 */
      glMatrixMode(GL_PROJECTION);
      
      /* 透視変換行列の初期化 */
      glLoadIdentity();
      gluPerspective(30.0, (double)w / (double)h, 1.0, 100.0);
      
      /* モデルビュー変換行列の指定 */
      glMatrixMode(GL_MODELVIEW);
      }
      
      void keyboard(unsigned char key, int x, int y)
{
  /* ESC か q をタイプしたら終了 */
  if (key == '\033' || key == 'q') {
    exit(0);
  }
}

void init(void)
{
  /* 初期設定 */
  glClearColor(1.0, 1.0, 1.0, 0.0);	         /* 画面を塗りつぶす色の設定 */
  glEnable(GL_DEPTH_TEST);			/* 陰面消去処理を有効にする */
  glEnable(GL_LIGHTING);			/* 陰影付けを有効にする */
  glEnable(GL_LIGHT0);			/* 光源の0番目を有効にする */
  glEnable(GL_CULL_FACE);			/* カリング処理を有効にする */
  glCullFace(GL_BACK);			/* 裏面を表示しないようにする */
}

void idle(void)
{
  glutPostRedisplay();			/* 再描画するイベント発生させる */
}

int main(int argc, char *argv[])
{
  glutInit(&argc, argv);			/* GLUTおよび,OpenGL環境の初期化 */

  /* GLUT_RGBA:色の指定をRGBで指定 GLUT_DEPTH:陰面消去処理を有効にする GLUT_DOUBLE:ダブルバッファリングを有効にする*/
  glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);	/* 表示モードの設定 */

  glutCreateWindow(argv[0]);	/* ウィンドウを開く(引数はタイトルバーに表示する文字) */
  glutDisplayFunc(display);		/* 再描画する場合に引数の関数が実行される */
  glutReshapeFunc(resize);		/* ウィンドウがリサイズされたときに引数の関数を実行する */
  glutIdleFunc(idle);		/* 操作などが無い場合に引数の関数が実行される */
  glutKeyboardFunc(keyboard);	/* キーボードをタイプした時に実行される */
  init();				/* 初期設定 */
  glutMainLoop();		         /* 無限ループ,待ち状態になる */
  return 0;
}