#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>

int spin = 0;				// 라이트 객체를 회전할 각도
int rot = 0;				// 큐브 객체를 회전할 각도(8개 전체에 적용)
bool bCube[8] = {0};		// 회전될 큐브 객체선택 
int direct = 1;				// 회전 방향 선택
int r_x=0, r_y=0, r_z=0;	// 회전 축
int t_x=15, t_y=0, t_z=0;   // 시점 위치
char ob_ch = 0;				// 키 입력값

GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };	// 라이트 
GLfloat low_shininess[] = { 2.0 };					// 라이트
GLfloat cube1[] = {-2.0, -2.0, -2.0};				// 큐브객체 1
GLfloat cube2[] = {-2.0, -2.0, 2.0};				// 큐브객체 2
GLfloat cube3[] = {-2.0, 2.0, -2.0};				// 큐브객체 3
GLfloat cube4[] = {-2.0, 2.0, 2.0};					// 큐브객체 4
GLfloat cube5[] = {2.0, -2.0, -2.0};				// 큐브객체 5
GLfloat cube6[] = {2.0, -2.0, 2.0};					// 큐브객체 6
GLfloat cube7[] = {2.0, 2.0, -2.0};					// 큐브객체 7
GLfloat cube8[] = {2.0, 2.0, 2.0};					// 큐브객체 8
GLfloat position[] = { 0.0, 0.0, 7.0, 1.0 };		// 라이트 위치

// 큐브 객체 색지정을 위한 8가지 색
GLfloat cube_color[8][3] ={{0,0,0},{0,0,1},{0,1,0},{0,1,1},
								{1,0,0},{1,0,1},{1,1,0},{1,1,1}};
int color_num[8] = {0,1,2,3,4,5,6,7};

void object_chang(char c);		// 객체 색 변경 모듈

void init(void)					
{
	glClearColor (0.0, 0.0, 1.0, 0.0);
	glShadeModel (GL_SMOOTH);
	glEnable(GL_LIGHT0);		// 광원 사용
	glEnable(GL_DEPTH_TEST);	// 깊이 검사를 함(먼곳에 있는 객체는 가까운 객체에 가려짐)
	glEnable(GL_COLOR_MATERIAL);	// 객체의 칼라를 활성화
}
	

void display(void){
	glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);		// 반사 효과
	glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);	// 반사 효과

	glPushMatrix ();
	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// 버퍼를 지운다.
	
	gluLookAt (t_x, t_y, t_z, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);	// 시점

// 라이트 방향 및 위치
	glPushMatrix ();
		glRotated ((GLdouble) spin, 1.0, 1.0, 0.0);		
		glLightfv (GL_LIGHT0, GL_POSITION, position);	// 광원의 번호와 위치 배열 설정

		glTranslated (0.0, 0.0, 7.0);
		glColor3f (1.0, 1.0, 1.0);
		glScalef (1.0, 1.0, 2.0);	  
		glutWireCube (0.5);
		glEnable (GL_LIGHTING);
	glPopMatrix ();

// 큐빅 중심 축
	glPushMatrix();
		glTranslatef(0,0,0);	
		glutSolidSphere(1.5,30,30);
	glPopMatrix();


// 여기 부터 8개 객체 큐빅
	glPushMatrix();
		glColor3fv (cube_color[color_num[0]]);			// 큐브 객체 색 지정
		if(bCube[0])
			glRotated ((GLdouble) rot, r_x, r_y, r_z);	// 큐브 객체 회전
		glTranslatef(cube1[0], cube1[1], cube1[2]);		// 큐브 객체 위치 지정
		glutSolidCube (3.0);							// 뷰브 생성
	glPopMatrix();
														// 이후 7개 객체 동일
	glPushMatrix();
		glColor3fv(cube_color[color_num[1]]);
		if(bCube[1])
			glRotated ((GLdouble) rot, r_x, r_y, r_z);
		glTranslatef (cube2[0], cube2[1], cube2[2]);
		glutSolidCube (3.0);
	glPopMatrix();

	glPushMatrix();
		glColor3fv(cube_color[color_num[2]]);
		if(bCube[2])
			glRotated ((GLdouble) rot, r_x, r_y, r_z);
		glTranslatef (cube3[0], cube3[1], cube3[2]);
		glutSolidCube (3.0);
	glPopMatrix();

	glPushMatrix();
		glColor3fv(cube_color[color_num[3]]);
		if(bCube[3])
			glRotated ((GLdouble) rot, r_x, r_y, r_z);
		glTranslatef (cube4[0], cube4[1], cube4[2]);
		glutSolidCube (3.0);
	glPopMatrix();

	glPushMatrix();
		glColor3fv(cube_color[color_num[4]]);
		if(bCube[4])
			glRotated ((GLdouble) rot, r_x, r_y, r_z);
		glTranslatef (cube5[0], cube5[1], cube5[2]);
		glutSolidCube (3.0);
	glPopMatrix();

	glPushMatrix();
		glColor3fv(cube_color[color_num[5]]);
		if(bCube[5])
			glRotated ((GLdouble) rot, r_x, r_y, r_z);
		glTranslatef (cube6[0], cube6[1], cube6[2]);
		glutSolidCube (3.0);
	glPopMatrix();

	glPushMatrix();
		glColor3fv(cube_color[color_num[6]]);
		if(bCube[6])
			glRotated ((GLdouble) rot, r_x, r_y, r_z);
		glTranslatef (cube7[0], cube7[1], cube7[2]);
		glutSolidCube (3.0);
	glPopMatrix();

	glPushMatrix();
		glColor3fv(cube_color[color_num[7]]);
		if(bCube[7])
			glRotated ((GLdouble) rot, r_x, r_y, r_z);
		glTranslatef (cube8[0], cube8[1], cube8[2]);
		glutSolidCube (3.0);
	glPopMatrix();
	glPopMatrix();


	HDC hDC;
	hDC=GetDC(FindWindow("GLUT","CUBIC"));
	char str1[100],str2[50],str3[50],str4[50];	

	sprintf(str1,"큐빅객체회전 - 좌우상하앞뒤 : (a,d,w,s,x,z)  전체 - 가로세로 : ( ],[ )");
	sprintf(str2,"시점변경 - 좌우상하 : (f,h,t,g) ");
	sprintf(str3,"라이트위치 : (q,e)");
	sprintf(str4,"회전방향설정 : (마우스 우측, 좌측 버튼)");
	TextOut(hDC, 10,10, str1, strlen(str1));
	TextOut(hDC, 10,30, str2, strlen(str2));
	TextOut(hDC, 10,50, str3, strlen(str3));
	TextOut(hDC, 10,70, str4, strlen(str4));	

	glFlush ();		// 화면에 실제 출력
}

void reshape (int w, int h)
{
	glViewport (0, 0, (GLsizei) w, (GLsizei) h); 
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ();
	glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 100.0);	// 깊이 100으로 지정
	glMatrixMode (GL_MODELVIEW);
}

void TimerFunc(int value){							// 자동으로 돌아가는 애니메이션 적용
	if(value > 0){ 
		if(direct == 1)		rot = (rot + 10) % 360;	// 양의 방향으로 10도 증가
		else if(direct == 2) rot = (rot - 10) % 360;// 음의 방향으로 10도 증가
		glutPostRedisplay();						// 화면 재 출력
		glutTimerFunc(10, TimerFunc, value-1);		// 90도 회전까지 재 호출
	}else{
		object_chang(ob_ch);						// 실제 객체 색 변경
		rot = 0;									// 회전 각도를 0으로 초기화
	}
}

void mouse(int button, int state, int x, int y)
{
	switch (button) {					// 마우스 좌,우 클릭으로 회전 방향 설정
		case GLUT_LEFT_BUTTON:
			if(state == GLUT_DOWN) {
				direct = 1;
			}
			break;
		case GLUT_RIGHT_BUTTON:
			if(state == GLUT_DOWN){
				direct = 2;
			}
			break;
		default:
			break;
	}
}

void keyboard(unsigned char key, int x, int y)
{
	int i, direct = 1;
	ob_ch = key;			// 입력받은 키값 저장
	switch (key) {
	case 'a' :							// 큐빅 좌측 부분 4개의 큐브 회전
		r_x = 0; r_y = 0; r_z = 1;
		for(i = 0; i < 8; i++){			// 큐빅 좌측 부분 선택
			if(i % 2) bCube[i] = true;	
			else bCube[i] = false;
		}
		break;
	case 'd' :							// 큐빅 우측 부분 4개의 큐브 회전
		r_x = 0; r_y = 0; r_z = 1;
		for(i = 0; i < 8; i++){
			if(i % 2) bCube[i] = false;
			else bCube[i] = true;
		}
		break;
	case 'z' :							// 큐빅 뒷부분 4개의 큐브 회전
		r_x = 1; r_y = 0; r_z = 0;
		for(i = 0; i < 8; i++){
			if(i < 4) bCube[i] = true;
			else bCube[i] = false;
		}
		break;
	case 'x' :							// 큐빅 앞부분 4개의 큐브 회전
		r_x = 1; r_y = 0; r_z = 0;
		for(i = 0; i < 8; i++){
			if(i < 4) bCube[i] = false;
			else bCube[i] = true;
		}
		break;
	case 'w' :							// 큐빅 윗부분 4개의 큐브 회전
		r_x = 0; r_y = 1; r_z = 0;
		for(i = 0; i < 8; i++){
			if(i % 4 < 2) bCube[i] = false;
			else bCube[i] = true;
		}
		break;
	case 's' :							// 큐빅 아래부분 4개의 큐브 회전
		r_x = 0; r_y = 1; r_z = 0;
		for(i = 0; i < 8; i++){
			if(i % 4 < 2) bCube[i] = true;
			else bCube[i] = false;
		}
		break;
	case '[' :							// 큐빅 세로 방향 전체 회전
		r_x = 0; r_y = 0; r_z = 1;
	    for(i = 0; i < 8; i++) bCube[i] = true;
		break;
	case ']' :							// 큐빅 가로 방향 전체 회전
		r_x = 0; r_y = 1; r_z = 0;
	    for(i = 0; i < 8; i++) bCube[i] = true;
		break;
	case 'f' :							// 카메라 좌측으로 이동
		t_z += 1;
		for(i = 0; i < 8; i++) bCube[i] = false;
		break;
	case 'h' :							// 카메라 우측으로 이동
		t_z -= 1;
		for(i = 0; i < 8; i++) bCube[i] = false;
		break;
	case 't' :							// 카메라 위쪽으로 이동	
		t_y += 1;
		for(i = 0; i < 8; i++) bCube[i] = false;
		break;
	case 'g' :							// 카메라 아래쪽으로 이동
		t_y -= 1;
		for(i = 0; i < 8; i++) bCube[i] = false;
		break;
	case 'q' :							// 라이트 회전
		spin = (spin + 10) % 360;
		for(i = 0; i < 8; i++) bCube[i] = false;
		direct = 0;
		glutPostRedisplay();
		break;
	case 'e' :							// 라이트 회전
		spin = (spin - 10) % 360;
		for(i = 0; i < 8; i++) bCube[i] = false;
		direct = 0;
		glutPostRedisplay();
		break;
	case 27:							// 종료
		exit(0);
		break;
	default :
		direct = 0;		
	}

	if(direct == 1 || direct == 2){
		glutTimerFunc(10, TimerFunc,9); // 속도 조절...
	}

}

void object_chang(char c){				// 객체 색 변경
	int temp;
	if(direct == 1){
		switch(c){
		case 'a' :
			temp = color_num[7];
			color_num[7] = color_num[5];
			color_num[5] = color_num[1];
			color_num[1] = color_num[3];
			color_num[3] = temp;
			break;
		case 'd' :
			temp = color_num[6];
			color_num[6] = color_num[4]; 
			color_num[4] = color_num[0];
			color_num[0] = color_num[2];
			color_num[2] = temp;
			break;
		case 'z' :
			temp = color_num[2];
			color_num[2] = color_num[0];
			color_num[0] = color_num[1];
			color_num[1] = color_num[3];
			color_num[3] = temp;
			break;
		case 'x' :
			temp = color_num[7];
			color_num[7] = color_num[6];
			color_num[6] = color_num[4];
			color_num[4] = color_num[5];
			color_num[5] = temp;
			break;
		case 's' :
			temp = color_num[5];
			color_num[5] = color_num[1];
			color_num[1] = color_num[0];
			color_num[0] = color_num[4];
			color_num[4] = temp;
			break;
		case 'w' :
			temp = color_num[7];
			color_num[7] = color_num[3];
			color_num[3] = color_num[2];
			color_num[2] = color_num[6];
			color_num[6] = temp;
			break;
		case '[' :
			temp = color_num[7];
			color_num[7] = color_num[5];
			color_num[5] = color_num[1];
			color_num[1] = color_num[3];
			color_num[3] = temp;
			temp = color_num[6];
			color_num[6] = color_num[4]; 
			color_num[4] = color_num[0];
			color_num[0] = color_num[2];
			color_num[2] = temp;
			break;
		case ']' :
			temp = color_num[7];
			color_num[7] = color_num[3];
			color_num[3] = color_num[2];
			color_num[2] = color_num[6];
			color_num[6] = temp;
			temp = color_num[5];
			color_num[5] = color_num[1];
			color_num[1] = color_num[0];
			color_num[0] = color_num[4];
			color_num[4] = temp;
			break;
		default :
			break;
		}
	}else if(direct == 2){
		switch(c){
		case 'a' :
			temp = color_num[7];
			color_num[7] = color_num[3];
			color_num[3] = color_num[1];
			color_num[1] = color_num[5];
			color_num[5] = temp;
			break;
		case 'd' :
			temp = color_num[6];
			color_num[6] = color_num[2]; 
			color_num[2] = color_num[0];
			color_num[0] = color_num[4];
			color_num[4] = temp;
			break;
		case 'z' :
			temp = color_num[2];
			color_num[2] = color_num[3];
			color_num[3] = color_num[1];
			color_num[1] = color_num[0];
			color_num[0] = temp;
			break;
		case 'x' :
			temp = color_num[7];
			color_num[7] = color_num[5];
			color_num[5] = color_num[4];
			color_num[4] = color_num[6];
			color_num[6] = temp;
			break;
		case 's' :
			temp = color_num[5];
			color_num[5] = color_num[4];
			color_num[4] = color_num[0];
			color_num[0] = color_num[1];
			color_num[1] = temp;
			break;
		case 'w' :
			temp = color_num[7];
			color_num[7] = color_num[6];
			color_num[6] = color_num[2];
			color_num[2] = color_num[3];
			color_num[3] = temp;
			break;
		case '[' :
			temp = color_num[7];
			color_num[7] = color_num[3];
			color_num[3] = color_num[1];
			color_num[1] = color_num[5];
			color_num[5] = temp;
			temp = color_num[6];
			color_num[6] = color_num[2]; 
			color_num[2] = color_num[0];
			color_num[0] = color_num[4];
			color_num[4] = temp;
			break;
		case ']' :
			temp = color_num[7];
			color_num[7] = color_num[6];
			color_num[6] = color_num[2];
			color_num[2] = color_num[3];
			color_num[3] = temp;
			temp = color_num[5];
			color_num[5] = color_num[4];
			color_num[4] = color_num[0];
			color_num[0] = color_num[1];
			color_num[1] = temp;
			break;
		default :
			break;
		}
	}
}



int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
	glutInitWindowSize (500, 500); 
	glutInitWindowPosition (100, 100);
	glutCreateWindow ("CUBIC");
	init ();
	glutDisplayFunc(display); 
	glutReshapeFunc(reshape);
	glutMouseFunc(mouse);
	glutKeyboardFunc(keyboard);
	glutMainLoop();
	return 0;
}


