#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

typedef enum colour{
	white = 1,
	red = 2,
	green = 3,
	blue = 4,
	yellow = 5,
	orange = 6
}colour;

typedef enum cmd{
	quit,
	run_state,
	goto_state,
	print_state,
	load,
	none
}cmd;

typedef struct Data{
	colour c[6][9];
	int times;
	int action;
	struct Data * next[7];
}Data;

void action0(colour s[6][9], colour d[6][9]);
void action1(colour s[6][9], colour d[6][9]);
void action2(colour s[6][9], colour d[6][9]);
void action3(colour s[6][9], colour d[6][9]);
void action4(colour s[6][9], colour d[6][9]);
void action5(colour s[6][9], colour d[6][9]);
void action6(colour s[6][9], colour d[6][9]);

void (*action[])(colour s[6][9], colour d[6][9])={
	action0,
	action1,
	action2,
	action3,
	action4,
	action5,
	action6
};

char buf[100];
int max_state = 0;
Data s;

cmd get_cmd(void)
{
	printf("get_cmd: ");
	scanf("%s",	buf);
	printf("cmd %s\n", buf);
	if(!strcmp(buf,"e")) return quit;
	if(!strcmp(buf,"p")) return print_state;
	if(!strcmp(buf,"r")) return run_state;
	if(!strcmp(buf,"g")) return goto_state;
	if(!strcmp(buf,"l")) return load;
	if(!strcmp(buf,"q")) return quit;
	
	return none;
	
}

void print_colour(colour p[6][9], int i)
{
	int j;
	if(i >= 0 && i <= 5){
		for(j = 0; j < 9; j++){
			switch(p[i][j]){
				case white:
					printf("white ");
					break;
				case red:
					printf("red ");
					break;
				case green:
					printf("green ");
					break;
				case blue:
					printf("blue ");
					break;
				case yellow:
					printf("yellow ");
					break;
				case orange:
					printf("orange ");
					break;
			}
		}
		printf("\n");
	}
	else{
		for(i = 0; i < 6; i++){
			for(j = 0; j < 9; j++){
				switch(p[i][j]){
					case white:
						printf("white ");
						break;
					case red:
						printf("red ");
						break;
					case green:
						printf("green ");
						break;
					case blue:
						printf("blue ");
						break;
					case yellow:
						printf("yellow ");
						break;
					case orange:
						printf("orange ");
						break;
				}
			}
			printf("\n");
		}
	}
}
void print_data(Data *p)
{
	printf("times = %d; action = %d\n", p->times, p->action);
	print_colour(p->c, 9);
}

int load_config(char * file_name, colour s[6][9])
{
	FILE * fp;
	int i, j;
	
	fp = fopen(file_name, "r");
	
	if(!fp){
		printf("failed to open file %s\n", file_name);
		return -1;
	}
	for(i = 0; i < 6; i++){
		char color[30];
		int n = 0;
		n = 0;
		fgets(buf, 100, fp);
		for(j = 0; j < 9; j++){
			int m = 0;
			m = 0;
			while(buf[n] != ' ') color[m++] = buf[n++];
			color[m] = 0;
			n++;
			if(!strcmp(color,"white")) s[i][j] = white;
			if(!strcmp(color,"red")) s[i][j] = red;
			if(!strcmp(color,"green")) s[i][j] = green;
			if(!strcmp(color,"blue")) s[i][j] = blue;
			if(!strcmp(color,"yellow")) s[i][j] = yellow;
			if(!strcmp(color,"orange")) s[i][j] = orange;
		}
	}
	
	fclose(fp);
	return 0;
}

int data_cmp(Data *pa, Data *pb)
{
	int i, j;
	for(i = 0; i < 6; i++){
		for(j = 0; j < 6; j++){
			if(pa->c[i][j] != pb->c[i][j]) return -1;
		}
	}
	return 0;
}

Data * data_search(Data *ps, Data *pd)
{
	int i;Data * p;
	if(!data_cmp(ps,pd)) return ps;
	for(i = 6; i > 0; i--){
		if(ps->next[i] == NULL) continue;
		if((p = data_search(ps->next[i], pd)) != NULL) return p;
	}
	return NULL;
}

void go(Data * p_s, Data * p_d, int i)
{
	if(i < 0 || i > 6) i = 0;
	(*action[i])(p_s->c, p_d->c);
	p_d->action = i;
	p_d->times = p_s->times + 1;
	p_d->next[i = 0] = p_s;
	while((++i) <= 6){
		p_d->next[i] = NULL;
	}
}

void run_next_state(Data * p)
{
	int i, j;
	Data * next;
	Data * d;
	for(i = 1; i <= 6; i++){
		next = (Data *)malloc(sizeof(Data));
		go(p, next, i);
		if((d = data_search(&s, next)) != 0){
			if( next->times < d->times){
				p->next[i] = next;
				for(j = 6; j > 0; j--){
				 	if(d->next[0]->next[j] == d){
				 		d->next[0]->next[j] = NULL;
				 		free(d);
				 		break;
				 	}
				}
			}
		}
		else p->next[i] = next;
	}
}

void go_state(Data *p ,int level, void function(Data *))
{
	int i;
	if(p->times == level){
		function(p);
	}
	else if(p->times < level){
		for(i = 6 ; i > 0; i--){
			if(p->next[i] != NULL) go_state(p->next[i],level,function);
		}
	}
}

void print_tree(Data * p)
{
	int i;Data *q;
	for(i = 6; i > 0; i--){
		if(p->next[i] != NULL) break;
	}
	if(i == 0){
		q = p;
		while(1){
			printf("times = %d; action = %d\n", q->times, q->action);
//			print_data(q);
			if(q->next[0] == NULL){
				print_colour(p->c, 9);
				break;
			}
			q = q->next[0];
		}
		printf("\n");
	}
	else{
		for(i = 6; i > 0; i--){
			if(p->next[i] != NULL) print_tree(p->next[i]);
		}
	}
}

int main (int argc, char *argv[])
{

	cmd c;
	int i, j;
	char file[30];
	struct tm * t;
	time_t t_t;
	printf("argc = %d\n", argc);
	for(i = 0; i < argc; i++){
		printf("argv[%d] = %s\n", i, argv[i]);
	}
	
	if(argc > 1){
		load_config(argv[1], s.c);
	}
	s.times = 0;
	s.action = 0;
	for(i = 0; i <= 6; i++){
		s.next[i] = NULL;
	}
	while(quit != (c = get_cmd())){
		switch(c){
			case load:
				scanf("%s",file);
				load_config(file, s.c);
				break;
			case run_state:
				scanf("%d", &i);				
				go_state(&s, i, run_next_state);
				break;
			case goto_state:
				scanf("%d", &i);
				for(j = 0; j <= i; j++){
					t_t = time(NULL);
					t = localtime(&t_t); 
					printf("%d:%d:%d : go into state %d\n", t->tm_hour, t->tm_min, t->tm_sec, j);
					go_state(&s, j, run_next_state);
					t_t = time(NULL);
					t = localtime(&t_t);
					printf("%d:%d:%d : complete state %d\n\n",t->tm_hour, t->tm_min, t->tm_sec, j);
				}
				break;
			case print_state:
				scanf("%d", &i);
				if(i < 0){
					print_tree(&s);
				}
				else go_state(&s, i, print_data);
				break;
			default:
				break;
		}
	}
	
	return 0;
	
}
//===================================================================================================================
void left(colour s[6][9], colour d[6][9], int i)
{
	d[i][6] = s[i][0];
	d[i][3] = s[i][1];
	d[i][0] = s[i][2];
	d[i][7] = s[i][3];
	d[i][4] = s[i][4];
	d[i][1] = s[i][5];
	d[i][8] = s[i][6];
	d[i][5] = s[i][7];
	d[i][2] = s[i][8];
}

void right(colour s[6][9], colour d[6][9], int i)
{
	d[i][2] = s[i][0];
	d[i][5] = s[i][1];
	d[i][8] = s[i][2];
	d[i][1] = s[i][3];
	d[i][4] = s[i][4];
	d[i][7] = s[i][5];
	d[i][0] = s[i][6];
	d[i][3] = s[i][7];
	d[i][6] = s[i][8];
}

void action0(colour s[6][9], colour d[6][9])
{
	int i, j;
	
	for(i = 0; i < 6; i++){
		for(j = 0; j < 9; j++){
			d[i][j] = s[i][j];
		}
	}
}
void action1(colour s[6][9], colour d[6][9])
{
	int i, j;
	
	for(j = 0; j < 9; j++){
		d[5][j] = s[5][j];
	}
	
	for(i = 0; i < 4; i++){
		for(j = 3; j < 9; j++){
			d[i][j] = s[i][j];
		}
	}
	
	for(i = 0; i < 3; i++){
		for(j = 0; j < 3; j++){
			d[i][j] = s[i+1][j];
		}
	}
	
	for(j = 0; j < 3; j++){
		d[3][j] = s[0][j];
	}
	
	right(s, d, 4);


}

void action2(colour s[6][9], colour d[6][9])
{
		int i, j;
	
	for(j = 0; j < 9; j++){
		d[4][j] = s[4][j];
	}
	
	for(i = 0; i < 4; i++){
		for(j = 0; j < 6; j++){
			d[i][j] = s[i][j];
		}
	}
	
	for(i = 0; i < 3; i++){
		for(j = 6; j < 9; j++){
			d[i][j] = s[i+1][j];
		}
	}
	
	for(j = 6; j < 9; j++){
		d[3][j] = s[0][j];
	}
	
	left(s, d, 5);
	
}

void action3(colour s[6][9], colour d[6][9])
{
	int i, j;
	
	for(i = 0; i < 6; i ++){
		for(j = 0; j < 9; j++){
			if(i == 3){
				d[i][j] = s[i][j];
			}
			else if(i == 0 || i == 4 || i == 5){
				if(j != 2 && j != 5 && j != 8){
					d[i][j] = s[i][j];
				}
			}
			else if(i == 2 && j != 0 && j != 3 && j != 6){
				d[i][j] = s[i][j];
			}
		}
	}
	
	for(j = 2; j < 9; j += 3){
		d[0][j] = s[5][j];
		d[4][j] = s[0][j];
		d[2][j-2] = s[4][j];
//		d[5][j] = s[2][j-2];
	}
	d[5][8] = s[2][0];
	d[5][5] = s[2][3];
	d[5][2] = s[2][6];

	right(s, d, 1);
	
}
void action4(colour s[6][9], colour d[6][9])
{
		int i, j;
	
	for(i = 0; i < 6; i ++){
		for(j = 0; j < 9; j++){
			if(i == 1){
				d[i][j] = s[i][j];
			}
			else if(i == 0 || i == 4 || i == 5){
				if(j != 0 && j != 3 && j != 6){
					d[i][j] = s[i][j];
				}
			}
			else if(i == 2 && j != 2 && j != 5 && j != 8){
				d[i][j] = s[i][j];
			}
		}
	}
	
	for(j = 0; j < 9; j += 3){
		d[0][j] = s[5][j];
		d[4][j] = s[0][j];
		d[2][j+2] = s[4][j];
//		d[5][j] = s[2][j+2];
	}
	d[5][0] = s[2][8];
	d[5][3] = s[2][5];
	d[5][6] = s[2][2];

	left(s, d, 3);
}
void action5(colour s[6][9], colour d[6][9])
{
	int i, j;
	for(i = 0; i < 6; i++){
		for(j = 0; j < 9; j++){
			switch(i){
				case 1:
					if(j != 0 && j != 3 && j != 6) d[i][j] = s[i][j];
					break;
				case 2:
					d[i][j] = s[i][j];
					break;
				case 3:
					if(j != 2 && j != 5 && j != 8) d[i][j] = s[i][j];
					break;
				case 4:
					if(j != 6 && j != 7 && j != 8) d[i][j] = s[i][j];
					break;
				case 5:
					if(j != 0 && j != 1 && j != 2) d[i][j] = s[i][j];
					break;
				default:
					break;
			}
		}
	}
	
	d[1][0] = s[5][2];
	d[1][3] = s[5][1];
	d[1][6] = s[5][0];
	d[4][6] = s[1][0];
	d[4][7] = s[1][3];
	d[4][8] = s[1][6];
	d[3][2] = s[4][8];
	d[3][5] = s[4][7];
	d[3][8] = s[4][6];
	d[5][0] = s[3][2];
	d[5][1] = s[3][5];
	d[5][2] = s[3][8];
	
	left(s, d, 0);
}

void action6(colour s[6][9], colour d[6][9])
{
	int i, j;
	for(i = 0; i < 6; i++){
		for(j = 0; j < 9; j++){
			switch(i){
				case 3:
					if(j != 0 && j != 3 && j != 6) d[i][j] = s[i][j];
					break;
				case 0:
					d[i][j] = s[i][j];
					break;
				case 1:
					if(j != 2 && j != 5 && j != 8) d[i][j] = s[i][j];
					break;
				case 5:
					if(j != 6 && j != 7 && j != 8) d[i][j] = s[i][j];
					break;
				case 4:
					if(j != 0 && j != 1 && j != 2) d[i][j] = s[i][j];
					break;
				default:
					break;
			}
		}
	}
	
	d[3][0] = s[4][2];
	d[3][3] = s[4][1];
	d[3][6] = s[4][0];

	d[5][6] = s[3][0];
	d[5][7] = s[3][3];
	d[5][8] = s[3][6];

	d[1][2] = s[5][8];
	d[1][5] = s[5][7];
	d[1][8] = s[5][6];

	d[4][0] = s[1][2];
	d[4][1] = s[1][5];
	d[4][2] = s[1][8];
	
	right(s, d, 2);
}

