#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<ctype.h>
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<memory.h>
#include<string.h>
#include<fcntl.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<time.h>
#include<sys/types.h>
#include<sys/mman.h>
#include <rtai.h>
#include <rtai_lxrt.h>
#include <rtai_config.h>
#include <sys/stat.h>
#include <rtai_mbx.h>


typedef struct position
{
    int px;
    int py;
}foodPos;

typedef struct node
{
    int x;
    int y;
    struct node *back;
    struct node *next;
}node;

char game[36][120];
int x1[50],y1[50];
int x2[50],y2[50];
node *head,*tail;
foodPos food;
void *tret;
int lenth;
int dir=4;
int l=1,f;
int life=3,score=0;
RT_TASK *task1;
RT_TASK *task2;
RT_TASK *task3;
RTIME period;
SEM *sem;
int push;
struct rt_mailbox *btn_mbox;

void initScr(){
    int i,j;
    for(i=0;i<36;i++){
                for(j=0;j<120;j++){
                    game[i][j]=' ';
                }
    }

    for(i=1;i<36;i++)
    {
        game[i][0]='#';
        game[i][119]='#';
    }

    for(i=0;i<120;i++){
    game[1][i]='#';
    game[35][i]='#';
    }

    game[0][1]='S';
    game[0][2]='C';
    game[0][3]='O';
    game[0][4]='R';
    game[0][5]='E';
    game[0][6]=':';
    game[0][7]=score;

    game[0][11]='L';
    game[0][12]='I';
    game[0][13]='F';
    game[0][14]='E';
    game[0][15]=':';
    game[0][16]=life;

}

void initsnake(){
	int x1,y1;
    node *t = (node *)malloc( sizeof(node) );
    head = (node *)malloc( sizeof(node) );
    tail = (node *)malloc( sizeof(node) );
    lenth=2;
    srand((int)time(0));
    head->x=rand()%32+2;
    head->y=rand()%123+3;
     x1=head->x;
     y1=head->y;
     head->back=tail->next=NULL;
     head->next=t;
     t->next=tail;
     tail->back=t;
     t->back=head;
     t->x=x1;
     tail->x=x1;
     t->y=y1-1;
     tail->y=y1-2;
}

void insert(x,y){
     node *t = (node *)malloc( sizeof(node) );

     t->x=x;
     t->y=y;

        t->next=head->next;
        head->next=t;
        t->back=head;
        t->next->back=t;
}

void printdata(){
    int i,j;
    game[0][7]=score;

    if(life>0)
    game[0][16]=life;

    else
    game[0][16]=0;

    for(i=0;i<=36;i++){
        printf("%c[1A", 27);
        printf("%c[2K", 27);
    }
    printf("        ");
    for(i=1;i<7;i++){
    	printf("%c",game[0][i]);
    }
    printf("%d",game[0][7]);
    printf("        ");
    for(i=11;i<16;i++){
        	printf("%c",game[0][i]);
        }
    printf("%d",game[0][16]);
    printf("\n");

    for(i=1;i<36;i++){
        for(j=0;j<120;j++){
    printf("%c",game[i][j]);
        }
    printf("\n");
    }
}

void delete(){
    node *t = (node *)malloc( sizeof(node) );
    node *t1 = (node *)malloc( sizeof(node) );
    tail->back=t1;
    t1->back=t;
    tail->back=t;
    t->next=tail;
    t1->back=t1->next=NULL;
    free(t1);
}

void clean(){
	int i,j;
	l=0;
	life=0;

    for(i=1;i<36;i++)
    	for(j=0;j<120;j++){
    		game[i][j]=' ';
    	}
    game[2][0]='G';
    game[2][1]='A';
    game[2][2]='M';
    game[2][3]='E';
    game[2][5]='O';
    game[2][6]='V';
    game[2][7]='E';
    game[2][8]='R';
    while(1){
    	printdata();
    	sleep(1);
    }


}
void continuemove(){
	int i,j;
	int cx,cy;
	node *t = (node *)malloc( sizeof(node) );
	for(i=2;i<35;i++){
		for(j=1;j<119;j++)
		game[i][j]=' ';
	}
	game[food.px][food.py]='*';
	head->x=6;
	head->y=lenth+10;
	x1[0]=6;
	y1[0]=head->y;
	for(i=1;i<=lenth;i++){
		x1[i]=6;
		y1[i]=lenth+10-i;
	}
	cx=x1[0];
    cy=y1[0];

	for(i=1;i<=lenth;i++){
			cx=x1[i];
			cy=y1[i];
		}
	dir=4;
	 t->next=head;
	 head->back=t;
	 for(i=0;i<=lenth;i++){
	         t=t->next;
	         t->x=x1[i];
	         t->y=y1[i];
	        }
}

void *moveinfo(void *arg){
	    unsigned long t_name;
		RTIME period1;
		int fd;
		unsigned long *ptr;


		fd = open("/dev/mem", O_RDWR);
		ptr = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0x80840000);
		ptr += 5;
		*ptr |= 0x00;
		ptr -= 4;

		*ptr=0x02;

		period1 = nano2count(100000000);
		t_name = nam2num("b0");
		task2 = rt_task_init(t_name, 1, 512, 256);
		rt_task_make_periodic(task2, rt_get_time(), period1);

		while(1){

			       if ((~(*ptr) & 0x01) == 0x01){
								push = 1;
								rt_mbx_send(btn_mbox, &push, sizeof(push));

							}



			       if ((~(*ptr) & 0x02) == 0x02){
								push = 2;
								rt_mbx_send(btn_mbox, &push, sizeof(push));

							}




			       if ((~(*ptr) & 0x04) == 0x04){
								push = 3;
								rt_mbx_send(btn_mbox, &push, sizeof(push));

							}



			       if ((~(*ptr) & 0x08) == 0x08){
								push = 4;
								rt_mbx_send(btn_mbox, &push, sizeof(push));

							}

			rt_task_wait_period();
		}
	}


void judge(){
   int i;
   int xhead,yhead;
   node *t = (node *)malloc( sizeof(node) );
   l=1;
   if(head->x==2 && dir==1){
	   l=0;
	   life--;
	   if(life>0)
	   continuemove();

	   else
       clean();
   }
   else if(head->x==34 && dir==2){
	       l=0;
	       life--;
	   	   if(life>0)
	   	   continuemove();

	   	   else
	       clean();
   }
   else if(head->y==1 && dir==3){
	       l=0;
	       life--;
	   	   if(life>0)
	   	   continuemove();

	   	   else
	       clean();
   }
   else if(head->y==118 && dir==4){
	       l=0;
	       life--;
	   	   if(life>0)
	   	   continuemove();

	   	   else
	       clean();
   }


   xhead=head->x;
   yhead=head->y;

   t->next=head;
   head->back=t;

   for(i=0;i<=lenth;i++){
        t=t->next;
        x1[i]=t->x;
        y1[i]=t->y;
       }


   if(lenth>2){
   for(i=2;i<lenth;i++){

       if(xhead-x1[i]==1 && yhead-y1[i]==0 && dir==1){
    	       l=0;
    	       life--;
    	   	   if(life>0)
    	   	   continuemove();

    	   	   else
    	       clean();
       }
       else if(x1[i]-xhead==1 && yhead-y1[i]==0 && dir==2){
    	       l=0;
    	       life--;
    	   	   if(life>0)
    	   	   continuemove();

    	   	   else
    	       clean();
       }
       else if(yhead-y1[i]==1 && xhead-x1[i]==0 && dir==3){
    	       l=0;
    	       life--;
    	   	   if(life>0)
    	   	   continuemove();

    	   	   else
    	       clean();
             }
       else if(y1[i]-yhead==1 && xhead-x1[i]==0 && dir==4){
    	   l=0;
    	   life--;
    	   if(life>0)
    	   continuemove();

    	   else
    	   clean();
       }
   }
   }


}



void up(){
	  int   cx,cy;
	  int i,j,x,y;
			        node *t = (node *)malloc( sizeof(node) );
			        x=head->x;
			        y=head->y;

			        t->next=head;
			        head->back=t;

			        dir=1;
			        judge();

			        x=head->x;
			        y=head->y;

			        if(l==1){
			        if(food.px==x-1 && food.py==y){
			        	    score++;
			        	    f=0;
			                insert(x,y);
			                lenth++;
			                for(i=0;i<=lenth;i++){

			                         t=t->next;
			                         if(i==0){
			                         t->x=x1[i]-1;
			                         t->y=y1[i];
			                          game[t->x][t->y]='%';
			                                 }
			                          else{
			                            t->x=x1[i-1];
			                            t->y=y1[i-1];
			                            game[t->x][t->y]='*';
			                                           }
			                }
			        }
			                else{
			                	f=1;
			                insert(x,y);
			                delete();
			                for(i=0;i<=lenth+1;i++){

			                              t=t->next;
			                              if(i==0){
			                              t->x=x1[i]-1;
			                              t->y=y1[i];
			                              game[t->x][t->y]='%';
			                              }
			                              else if(i==lenth+1){
			                           	   cx=x1[i-1];
			                           	   cy=y1[i-1];
			                           	   game[cx][cy]=' ';
			                              }
			                              else{
			                           	   t->x=x1[i-1];
			                           	   t->y=y1[i-1];
			                               game[t->x][t->y]='*';
			                           }

			                }
			                }
		}
}

void down(){
	  int   cx,cy;
		  int i,j,x,y;
		        node *t = (node *)malloc( sizeof(node) );
		        x=head->x;
		        y=head->y;

		        t->next=head;
		        head->back=t;

		        dir=2;
		        judge();

		        x=head->x;
		        y=head->y;

		        if(l==1){
		        if(food.px==x+1 && food.py==y){
		        	    score++;
		        	    f=0;
		                insert(x,y);
		                lenth++;
		                for(i=0;i<=lenth;i++){

		                         t=t->next;
		                         if(i==0){
		                         t->x=x1[i]+1;
		                         t->y=y1[i];
		                          game[t->x][t->y]='%';
		                                 }
		                          else{
		                            t->x=x1[i-1];
		                            t->y=y1[i-1];
		                            game[t->x][t->y]='*';
		                                           }
		                }
		        }
		                else{
		                	f=1;
		                insert(x,y);
		                delete();
		                for(i=0;i<=lenth+1;i++){

		                              t=t->next;
		                              if(i==0){
		                              t->x=x1[i]+1;
		                              t->y=y1[i];
		                              game[t->x][t->y]='%';
		                              }
		                              else if(i==lenth+1){
		                           	   cx=x1[i-1];
		                           	   cy=y1[i-1];
		                           	   game[cx][cy]=' ';
		                              }
		                              else{
		                           	   t->x=x1[i-1];
		                           	   t->y=y1[i-1];
		                               game[t->x][t->y]='*';
		                           }

		                }
		                }
	}

}
void left(){
	  int   cx,cy;
		  int i,j,x,y;
		        node *t = (node *)malloc( sizeof(node) );
		        x=head->x;
		        y=head->y;

		        t->next=head;
		        head->back=t;

		        dir=3;
		        judge();

		        x=head->x;
		        y=head->y;
		        if(l==1){
		        if(food.py==y-1 && food.px==x){
		        	score++;
		        	    f=0;
		                insert(x,y);
		                lenth++;
		                for(i=0;i<=lenth;i++){

		                         t=t->next;
		                         if(i==0){
		                         t->x=x1[i];
		                         t->y=y1[i]-1;
		                          game[t->x][t->y]='%';
		                                 }
		                          else{
		                            t->x=x1[i-1];
		                            t->y=y1[i-1];
		                            game[t->x][t->y]='*';
		                                           }
		                }
		        }
		                else{
		                	f=1;
		                insert(x,y);
		                delete();
		                for(i=0;i<=lenth+1;i++){

		                              t=t->next;
		                              if(i==0){
		                              t->x=x1[i];
		                              t->y=y1[i]-1;
		                              game[t->x][t->y]='%';
		                              }
		                              else if(i==lenth+1){
		                           	   cx=x1[i-1];
		                           	   cy=y1[i-1];
		                           	   game[cx][cy]=' ';
		                              }
		                              else{
		                           	   t->x=x1[i-1];
		                           	   t->y=y1[i-1];
		                               game[t->x][t->y]='*';
		                           }

		                }
		                }
		        }
}

void right(){
	  int   cx,cy;
	  int i,j,x,y;
	        node *t = (node *)malloc( sizeof(node) );
	        x=head->x;
	        y=head->y;

	        t->next=head;
	        head->back=t;

	        dir=4;
	        judge();

	        x=head->x;
	        y=head->y;

	        if(l==1){
	        if(food.py==y+1 && food.px==x){
	        	    score++;
	        	    f=0;
	                insert(x,y);
	                lenth++;
	                for(i=0;i<=lenth;i++){

	                         t=t->next;
	                         if(i==0){
	                         t->x=x1[i];
	                         t->y=y1[i]+1;
	                          game[t->x][t->y]='%';
	                                 }
	                          else{
	                            t->x=x1[i-1];
	                            t->y=y1[i-1];
	                            game[t->x][t->y]='*';
	                                           }
	                }
	        }
	                else{
	                	f=1;
	                insert(x,y);
	                delete();
	                for(i=0;i<=lenth+1;i++){

	                              t=t->next;
	                              if(i==0){
	                              t->x=x1[i];
	                              t->y=y1[i]+1;
	                              game[t->x][t->y]='%';
	                              }
	                              else if(i==lenth+1){
	                           	   cx=x1[i-1];
	                           	   cy=y1[i-1];
	                           	   game[cx][cy]=' ';
	                              }
	                              else{
	                           	   t->x=x1[i-1];
	                           	   t->y=y1[i-1];
	                               game[t->x][t->y]='*';
	                           }

	                }
	                }
}
}





void *movement(void *arg){

    period= start_rt_timer(nano2count(100000000));
    task1= rt_task_init(nam2num("task1"), 1, 512, 256);
    rt_task_make_periodic(task1, rt_get_time(), 5*period);

    while(1){
	//rt_sem_wait(sem);


    if(l==1){
	rt_mbx_receive(btn_mbox, &push, sizeof(push));

    if(push==1){
        up();
    }
    else if(push==2){
        down();
    }
    else if(push==3){
        left();
    }
    else if(push==4){
        right();
    }

    else
    	break;

    //rt_sem_signal(sem);
    }

    rt_task_wait_period();
  }
  pthread_exit(0);
}

void *refresh(void *arg){
	RTIME period2;
	int x,y,flag=1,i,c=2;
	period2= start_rt_timer(nano2count(300000000));
	task3= rt_task_init(nam2num("task3"), 0, 512, 256);
	rt_task_make_periodic(task3, rt_get_time(), period2);

	while(1){
		if(lenth>2){
		period2= start_rt_timer(nano2count(300000000-lenth*40000000));
		rt_task_make_periodic(task3, rt_get_time(), period2);
		}



		if(f==0){
		    game[food.px][food.py]=' ';
		    srand((int)time(0));
		    x=food.px=rand()%33+2;
		    y=food.py=rand()%117+1;

		    while(flag==1){
		    for(i=0;i<=lenth;i++){
		    	if(x==x1[i] && y==y1[i]){
		    		flag=1;
		    		srand((int)time(0));
		    		x=food.px=rand()%32+2;
		    	    y=food.py=rand()%117+1;
		    	    break;
		    	}
		    	else if(x==x1[i] && y==y1[i]+1){
		    			    		flag=1;
		    			    		srand((int)time(0));
		    			    		x=food.px=rand()%32+2;
		    			    	    y=food.py=rand()%117+1;
		    			    	    break;
		    			    	}
		    	else if(x==x1[i] && y==y1[i]-1){
		    			    		flag=1;
		    			    		srand((int)time(0));
		    			    		x=food.px=rand()%32+2;
		    			    	    y=food.py=rand()%117+1;
		    			    	    break;
		    			    	}
		    	else if(x==x1[i]+1 && y==y1[i]){
		    			    		flag=1;
		    			    		srand((int)time(0));
		    			    		x=food.px=rand()%32+2;
		    			    	    y=food.py=rand()%117+1;
		    			    	    break;
		    			    	}
		    	else if(x==x1[i]-1 && y==y1[i]){
		    			    		flag=1;
		    			    		srand((int)time(0));
		    			    		x=food.px=rand()%32+2;
		    			    	    y=food.py=rand()%117+1;
		    			    	    break;
		    			    	}
		    	else{
		    		flag=0;
		    		break;
		    	}
		    }

		    }

            f=1;
	        flag=1;
		    game[x][y]='*';
		   printdata();
		    }

          if(l==1){
		  if(dir==1)
		        up();
		  else if(dir==2)
		        down();
		  else if(dir==3)
		        left();
		  else if (dir==4)
		        right();
        }

         l=1;
		 printdata();

		 rt_task_wait_period();
	}

}

int main(){
    int i,j,n;

    pthread_t pthread1,pthread2,pthread3;
    sem=rt_typed_sem_init(nam2num("sem1"),1,BIN_SEM);

	btn_mbox = rt_typed_named_mbx_init("bmb", sizeof(push)*15, FIFO_Q);

      for(i=0;i<55;i++){
            printf(" ");
        }
        printf("***************\n");
        for(i=0;i<57;i++){
                printf(" ");
            }
        printf("Snake Game\n");
        for(i=0;i<55;i++){
                printf(" ");
            }
        printf("***************\n");

     A: printf("1.start game\n");
        printf("2.quit\n");

        scanf("%d",&n);
        switch(n){
            case 1:
                initScr();
                initsnake();
                system("clear");
                pthread_create(&pthread3, NULL, refresh , NULL);
                pthread_create(&pthread1, NULL, moveinfo, NULL);
                pthread_create(&pthread2, NULL, movement, NULL);
                pthread_join(pthread2,&tret);
                goto A;
                break;
            case 2:
                break;
            default:
                printf("fail,please choose again\n");
                printf("\n");
                goto A;
            }


    return 0;
}
