#include <startup.h>
#include  <stdio.h>
#include  <stdlib.h>
#include  <string.h>
#include  <math.h>
#include  <stdarg.h>
#include  <time.h>
#include <LPC214X.h>
#include <type.h>
#include <TFT018.h>

void HexStr(char *s, unsigned short h){
	unsigned char i;
	for(i=0;i<4;i++) {
		unsigned char c=(h>>(i*4))&0xF;
		c=(c>9)?'A'+c-10:'0'+c;
		s[3-i]=c;
		}
	s[4]=0;
	}

void DecStr(char *s,int d) {
	signed char t[20],i=0,l=0;
	if(d==0) 
	{
		s[0]='0';s[1]=0;return;
	}
	while(d>0) {
		t[i++]=d%10;
		d/=10;
		}
	s[i--]=0;
	for(;i>=0;i--) {
		s[i]=t[l++]+'0';
		}
	}

char s[20];
// Key
#define UP		0x02
#define DOWN	0x10
#define LEFT	0x20
#define RIGHT	0x08
#define OK		0x04
#define ESC		0x01
/*
#define UP		0x3D
#define DOWN	0x2F
#define LEFT	0x1F
#define RIGHT	0x37
#define OK		0x3B
#define ESC		0x3E
*/
//
void KeyInit(void) {
	IODIR0 &= 0xFFFF03FF;	// Set P0.10-P0.15 to input mode
	}

unsigned char ReadKey(void) {
	static int KeyCount=0;
	static char preKey=0x3F;
	char k=(unsigned char)(IOPIN0>>10) & 0x3F;
	if(k==preKey) {
		KeyCount++;	
		if(KeyCount>30000) {
			KeyCount=0;
			return k;	
			}
		}
	else {
		KeyCount=0;
		}
	preKey=k;
	return 0x3F;
	}

unsigned short M[3]={0,0x10,0x1F};
void pallette(void) {
	unsigned char r,g,b,p=0;
	unsigned short c;
	for(r=0;r<3;r++) {
		for(g=0;g<3;g++) {
			for(b=0;b<3;b++) {
				unsigned char x,y;
				c=(M[r]<<11)|(M[g]<<6)|M[b];
				x=(p%4)*32;
				y=(p/4)*16;
				HexStr(s,c);
				LCD_PutString(x,y,s,0xFFF&(~c),c);
				p++;
				}
			}
		}
	}

// Colors
#define BLOCK_COLOR		0xF800
#define EDGE_COLOR		0x8400
#define BOARD_COLOR		0x0000
#define FRAME_COLOR		0x0400
// Size
#define BLOCK_LENGTH	5
#define XMAX			12
#define YMAX			24
// Position
#define XORG			10
#define YORG			0
// Variable
signed char keycode=0;
signed char Board[XMAX+2][YMAX+2];
signed char Block[4][4];
signed char currentX,currentY;
signed char currentShape;
signed char fallOver;
signed char gameOver;
long Time;
long Score;
signed char currentGrade;
long Grade[10]={100000,85000,75000,55000,50000,
           40000,30000,9000,6000,4000};
// Const
struct {
  char xy[8];
  WORD color;
  char next;
} Shapes[19]={
	{1,1,1,2,1,3,2,3,BLOCK_COLOR,1},
	{2,2,0,3,1,3,2,3,BLOCK_COLOR,2},
	{1,1,2,1,2,2,2,3,BLOCK_COLOR,3},
	{0,2,1,2,2,2,0,3,BLOCK_COLOR,0},

	{1,1,2,1,1,2,1,3,BLOCK_COLOR,5},
	{0,2,0,3,1,3,2,3,BLOCK_COLOR,6},
	{2,1,2,2,1,3,2,3,BLOCK_COLOR,7},
	{0,2,1,2,2,2,2,3,BLOCK_COLOR,4},

	{1,2,0,3,1,3,2,3,BLOCK_COLOR,9},
	{1,1,0,2,1,2,1,3,BLOCK_COLOR,10},
	{0,2,1,2,2,2,1,3,BLOCK_COLOR,11},
	{1,1,1,2,2,2,1,3,BLOCK_COLOR,8},

	{1,2,2,2,0,3,1,3,BLOCK_COLOR,13},
	{0,1,0,2,1,2,1,3,BLOCK_COLOR,12},

	{0,2,1,2,1,3,2,3,BLOCK_COLOR,15},
	{1,1,0,2,1,2,0,3,BLOCK_COLOR,14},

	{1,0,1,1,1,2,1,3,BLOCK_COLOR,17},
	{0,3,1,3,2,3,3,3,BLOCK_COLOR,16},

	{1,2,2,2,1,3,2,3,BLOCK_COLOR,18}
	};

void DisplayScore(void) 
{
	LCD_PutString(80,10,"Grade",0xF800,0xFFE0);
	DecStr(s,currentGrade);
	LCD_PutString(80,30,s,0xF800,0xFFE0);
	
	LCD_PutString(80,60,"Score",0xF800,0xFFE0);
	DecStr(s,(int)Score);
	LCD_PutString(80,80,s,0xF800,0xFFE0);
}
//
void Bind2Block() {
	char i,j,x,y;
	for(i=0;i<4;i++)
		for(j=0;j<4;j++)
			Block[i][j]=0;
	for(i=0;i<8;i+=2) {
		x=Shapes[currentShape].xy[i];
		y=Shapes[currentShape].xy[i+1];
		Block[x][y]=1;
		}
	}

void bindtoBoard() {
	signed char i,x,y;
	for(i=0;i<8;i=i+2) {
		x=currentX+Shapes[currentShape].xy[i];
		y=currentY+Shapes[currentShape].xy[i+1];
		Board[x][y]=1;
		}
	}
//
int Judge(signed char code) {
	signed char i,x,y,next;
	switch(code){
		case -1: 
		for(i=0;i<8;i=i+2) {
			x=currentX+Shapes[currentShape].xy[i]-1;
			y=currentY+Shapes[currentShape].xy[i+1];
			if(Board[x][y]==1) return 0;
			}
		return 1;
		case  1:
		for(i=0;i<8;i=i+2) {
			x=currentX+Shapes[currentShape].xy[i]+1;
			y=currentY+Shapes[currentShape].xy[i+1];
			if(Board[x][y]==1) return 0;
			}
		return 1;
		case  8:
		next=Shapes[currentShape].next;
    	for(i=0;i<8;i=i+2) {
     		x=currentX+Shapes[next].xy[i];
     		y=currentY+Shapes[next].xy[i+1];
     		if(Board[x][y]==1) return 0;
    		}
    	return 1;

		case  5:
   		for(i=0;i<8;i=i+2) {
			x=currentX+Shapes[currentShape].xy[i];
			y=currentY+Shapes[currentShape].xy[i+1]+1;
			if(y<0) continue;
			if(Board[x][y]==1) return 0;
			} 		
    	return 1;

		case  4:
		return 1;
		default: 
		return 0;
		}
	}
//
void newBlock() {
	currentX=XMAX/2-2;
	currentY=-3;
	gameOver=0;
	fallOver=0;
//	currentShape=19*rand()/RAND_MAX;
	currentShape=rand()%19;
	Bind2Block();
	Time=Grade[currentGrade];
	if(Judge(5)==0) gameOver=1;
	}

void drawBoard() {
	signed char x,y,x1,y1;

	LCD_Clear_Screen(Blue);

	x=XORG+BLOCK_LENGTH;
	y=YORG+BLOCK_LENGTH;
	x1=x+XMAX*BLOCK_LENGTH;
	y1=y+YMAX*BLOCK_LENGTH;
	LCD_Rectangle(x-1,y-1,x1+1,y1+1,FRAME_COLOR);
	LCD_Rectangle(x,y,x1,y1,BOARD_COLOR);
	
	DisplayScore();
	}

void drawBlock(signed char x, signed char y, WORD color) {
	if(y<YORG+BLOCK_LENGTH) return;
	LCD_Rectangle(x,y, x+BLOCK_LENGTH,y+BLOCK_LENGTH,EDGE_COLOR);
	LCD_Rectangle(x+1,y+1, x+BLOCK_LENGTH-1,y+BLOCK_LENGTH-1,color);
	}
void eraseBlock() {
	signed char i,x,y,actx,acty;
 	for(i=0;i<8;i=i+2) {
  		x=currentX+Shapes[currentShape].xy[i];
  		y=currentY+Shapes[currentShape].xy[i+1];
  		if(y>0) {
   			actx=XORG+x*BLOCK_LENGTH;
   			acty=YORG+y*BLOCK_LENGTH;
   			LCD_Rectangle(actx,acty, actx+BLOCK_LENGTH,acty+BLOCK_LENGTH,BOARD_COLOR);
  			}
 		}
	}

void updateBoard() {
	signed char i,j,x,y;
	drawBoard();
	for(i=1;i<=YMAX;i++)
		for(j=1;j<=XMAX;j++)
			if(Board[j][i]) {
				x=XORG+j*BLOCK_LENGTH;
				y=YORG+i*BLOCK_LENGTH;
				drawBlock(x,y,BLOCK_COLOR);
				}
	}

void updateBlock() {
	signed char i,x,y,actx,acty;
	for(i=0;i<8;i=i+2) {
		x=currentX+Shapes[currentShape].xy[i];
		y=currentY+Shapes[currentShape].xy[i+1];
		actx=XORG+x*BLOCK_LENGTH;
		acty=YORG+y*BLOCK_LENGTH;
		drawBlock(actx,acty,Shapes[currentShape].color);
		}
	}

void InitData() {
	signed char i,j;
	gameOver=0;
	currentShape=rand()%19;
	Bind2Block();
	currentX=XMAX/2-2;
	currentY=-3;
	currentGrade=1;
	Score=0;
	Time=Grade[currentGrade-1];

	for(i=0;i<YMAX+2;i++)
		for(j=0;j<XMAX+2;j++)
			Board[j][i]=1;
	for(i=0;i<=YMAX;i++)
		for(j=1;j<=XMAX;j++)
			Board[j][i]=0;
	}

void clearLine() {
	signed char i,j,k,flag;
	signed char eraselines=0;
	for(i=1;i<=YMAX;i++) {
		flag=1;
		for(j=1;j<=XMAX;j++)
		if(Board[j][i]==0) {
			flag=0;
			break;
			}
		if(flag){
			for(j=1;j<=XMAX;j++) {
				k=i;
				while(k>1) {Board[j][k]=Board[j][k-1]; k--;}
				Board[j][1]=0;
				}
		eraselines++;
		updateBoard();
		}
	}
	switch(eraselines) {
		case 1:Score+=100;break;
		case 2:Score+=250;break;
		case 3:Score+=750;break;
		case 4:Score+=1000;break;
		default:break;
		}
	if(Score>(currentGrade*3000)) {
		currentGrade++;
		if(currentGrade>10) Time=10;
		}
	DisplayScore();
	}

void moveLeft() {
 	if(Judge(-1) == 0) return;
	eraseBlock();
	currentX--;
	updateBlock();
	}

void moveRight() {
 	if(Judge(1) == 0) return;
	eraseBlock();
	currentX++;
	updateBlock();
	}

void moveDown() {
	if(Judge(5) == 0) {
		fallOver=1;
		bindtoBoard();
			clearLine();
			if(currentY < 0) gameOver=1;
			return;
			}
	eraseBlock();
	currentY++;
	updateBlock();
	}

void turnUp() {
	if(Judge(8) == 0) return;
	eraseBlock();
	currentShape=Shapes[currentShape].next;
	Bind2Block();
	updateBlock();
	}

void slowUp() {
	while(Time>10) Time-=10;
	}

void straightDown() {
	while(Judge(5)) {
		moveDown();
		}
	bindtoBoard();
	clearLine();
	}

void dispatchMessage(signed char key) {
	switch(key) {
		case LEFT:  moveLeft();keycode=0; break;
		case RIGHT: moveRight();keycode=0; break;
		case UP:    turnUp();keycode=0; break;
		case OK:	slowUp();keycode=0;break;
		case DOWN:	straightDown();keycode=0;newBlock();break;
		case ESC: gameOver=1;break;
		default:
			keycode=0;
			Time--;
			if(Time==0){
				moveDown();
				if(fallOver) newBlock();
				Time=Grade[currentGrade-1];
				break;
				}
		}
	}

signed char getmessage() {
	keycode=(~ReadKey())&0x3F;
	return keycode;
	}


char bx=65,by=65,bw=10,bh=10;
int main(void) {
	Initialize();
	BacklightOn();
	LCD_Init();
	LCD_Clear_Screen(Blue);
	KeyInit();
//	PutString(1,30,"Welcome to see you!",0xF800,0xFFE0);
//	pallette();
	InitData();
	srand(123785);
	drawBoard();
 	do {
		getmessage();
		dispatchMessage(keycode);
		if(gameOver) {
			InitData();
			srand(123785);
			drawBoard();
			}

//	HexStr(s,(unsigned char)(IOPIN0>>10) & 0x3F );
//	PutString(80,100,s,0xF800,0xFFE0);
		} while(1);
	}

