#include <windows.h>
#include <stdlib.h>
#include <time.h>
#include "gtris.h"

#define W_WIDTH 425
#define W_HEIGHT 600

void p_update();
void init_double_buffer();
void drawScore(HDC);
void drawInfo(HDC);
void drawNext(HDC);
void drawBlocks(HDC);
void drawPaused(HDC);
void endGame();
void FlameThread(LPVOID);
void getpalette(RGBQUAD*);
void rollpalette();
int isHit(int,int,const short*);
void setBlock(int,int,int,int);
void testBounds(short, int);
void CALLBACK plink(HWND,UINT,UINT,DWORD);
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int);

const char szAppName[]= "Groovy Tetris";

enum state { INFO, RUNNING, PAUSED, OVER };
enum piecetype { EL, ELB, S, Z, BLOCK, BAR, TEE };
const short CHANGEY=0, CHANGEX=1, CHANGEROT=2;
const int BASE_SPEED=1000;
const short data[][4][16]= {
  {{0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1},{0,0,0,0,0,0,1,0,0,0,1,0,0,1,1,0},{0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,0},{0,0,0,0,0,0,1,1,0,0,1,0,0,0,1,0}}, //EL
  {{0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,0},{0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,1},{0,0,0,0,0,0,0,1,0,1,1,1,0,0,0,0},{0,0,0,0,0,1,1,0,0,0,1,0,0,0,1,0}}, //ELB
  {{0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,0},{0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0},{0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,0},{0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0}}, //S
  {{0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1},{0,0,0,0,0,0,1,0,0,1,1,0,0,1,0,0},{0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1},{0,0,0,0,0,0,1,0,0,1,1,0,0,1,0,0}}, //Z
  {{0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0},{0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0},{0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0},{0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0}}, //BLOCK
  {{0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0},{0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0},{0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0},{0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0}}, //BAR
  {{0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,0},{0,0,0,0,0,0,1,0,0,0,1,1,0,0,1,0},{0,0,0,0,0,0,1,0,0,1,1,1,0,0,0,0},{0,0,0,0,0,0,1,0,0,1,1,0,0,0,1,0}}  //TEE
};

HBRUSH colors[7];
HBRUSH smileyYellow,barBlack;

bool paused, gameOn;
int flameCount;
state mode;
int lines;
int score;
int speed;
int drawNextPiece=1;

short nextPiece;
short current;
short orientation;
short posx, posy; //position based on *center* of 3x3 grid. bar goes outside to the left/top
short grid[10][22]; //2 added to the top in the case of the bar being flipped up

DWORD TidFlame, TidPlink;
HMENU menu;
HDC memdc=NULL;
HWND hwnd;

typedef BYTE byte_t;

struct pBITMAPINFO{
	BITMAPINFOHEADER bmiHeader;
	RGBQUAD bmiColors[256];
}BMInfo;

struct pLOGPALETTE{
	WORD	palVersion;
	WORD	palNumEntries;
	PALETTEENTRY palPalEntry[256];
}PalInfo;

HBITMAP hBM;
byte_t* double_buffer;

void blit(void);

void p_update(){
	int i, j, index, flamecountplus;
	float jdivflamecountplus[75];
	flamecountplus = flameCount+75;
	for (i=0;i<75;i++)
		jdivflamecountplus[i]=i/flamecountplus;
	for (i=0,index=0;i<W_WIDTH;i++) for (j=0;j<W_HEIGHT;j++,index++)
		double_buffer[index]=(unsigned char)((i+flameCount-flameCount) & 0377);
			//(unsigned char)(((int)(i*jdivflamecountplus[j])+flameCount & 0377);
			//(unsigned char)((i*j/flameCount*75+flameCount) & 0377);
			//3;
}

void FlameThread(LPVOID param) {
	int step=2;
	while (true) {
		flameCount+=step;
		if (flameCount > 255 || flameCount < 0) {
			step*=-1;
			flameCount+=step;
		}
		rollpalette();
		p_update();
		blit();
		Sleep(1);
	}
}

void getpalette(RGBQUAD* p){
	int i;
	for(i=0;i<256;i++){
		p[i].rgbRed = (unsigned char)i;
		p[i].rgbGreen = (unsigned char)((i+100)%256);
		p[i].rgbBlue = (unsigned char)((i+200)%256);
//		p[i].rgbRed = i;
//		p[i].rgbGreen = i;
//		p[i].rgbBlue = i;
		p[i].rgbReserved = 0;
	}
}

void rollpalette() {
  int i;
  HPALETTE PalHan;
  HWND ActiveWindow;
  RGBQUAD palette[256];
  HDC hDC;
  for (i=0;i<256;i++) {
		palette[i].rgbRed = (unsigned char)(i-flameCount);
		palette[i].rgbGreen = (unsigned char)((i*(flameCount/30)+100)%256);
		palette[i].rgbBlue = (unsigned char)((i+200)%256%flameCount);
		palette[i].rgbReserved = 0;
  }
  ActiveWindow = GetActiveWindow();
  hDC = GetDC(ActiveWindow);
	for(i=0;i<256;i++)
		BMInfo.bmiColors[i] = palette[i];

	PalInfo.palVersion = 0x300;
	PalInfo.palNumEntries = 256;
	for(i=0;i<256;i++){
		PalInfo.palPalEntry[i].peRed = palette[i].rgbRed;
		PalInfo.palPalEntry[i].peGreen = palette[i].rgbGreen;
		PalInfo.palPalEntry[i].peBlue = palette[i].rgbBlue;
		PalInfo.palPalEntry[i].peFlags = PC_NOCOLLAPSE;
	}

		/* create the palette */
	PalHan = CreatePalette((LOGPALETTE*)&PalInfo);
		/* select it for that DC */
	SelectPalette(hDC,PalHan,FALSE);
		/* realize a palette on that DC */
	RealizePalette(hDC);
		/* delete palette handler */
	DeleteObject(PalHan);
	ReleaseDC(ActiveWindow,hDC);

}
		
void init_double_buffer(){
	HPALETTE PalHan;
	HWND ActiveWindow;
	HDC hDC;
	RGBQUAD palette[256];
	int i;

	ActiveWindow = GetActiveWindow();
	hDC = GetDC(ActiveWindow);

	BMInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	BMInfo.bmiHeader.biWidth = W_WIDTH;
	BMInfo.bmiHeader.biHeight = -abs(W_HEIGHT);
	BMInfo.bmiHeader.biPlanes = 1;
	BMInfo.bmiHeader.biBitCount = 8;
	BMInfo.bmiHeader.biCompression = BI_RGB;
	BMInfo.bmiHeader.biSizeImage = 0;
	BMInfo.bmiHeader.biXPelsPerMeter = 0;
	BMInfo.bmiHeader.biYPelsPerMeter = 0;
	BMInfo.bmiHeader.biClrUsed = 256;
	BMInfo.bmiHeader.biClrImportant = 256;

	getpalette(palette);

	for(i=0;i<256;i++)
		BMInfo.bmiColors[i] = palette[i];

	PalInfo.palVersion = 0x300;
	PalInfo.palNumEntries = 256;
	for(i=0;i<256;i++){
		PalInfo.palPalEntry[i].peRed = palette[i].rgbRed;
		PalInfo.palPalEntry[i].peGreen = palette[i].rgbGreen;
		PalInfo.palPalEntry[i].peBlue = palette[i].rgbBlue;
		PalInfo.palPalEntry[i].peFlags = PC_NOCOLLAPSE;
	}

		/* create the palette */
	PalHan = CreatePalette((LOGPALETTE*)&PalInfo);
		/* select it for that DC */
	SelectPalette(hDC,PalHan,FALSE);
		/* realize a palette on that DC */
	RealizePalette(hDC);
		/* delete palette handler */
	DeleteObject(PalHan);

	hBM = CreateDIBSection(hDC,(BITMAPINFO*)&BMInfo,
		DIB_RGB_COLORS,(void**)&double_buffer,0,0);
	ReleaseDC(ActiveWindow,hDC);
}

void drawScore(HDC hdc) {
  //then print the score... whee... :)
	char scoreString[255];
  char lineString[255];
  SetBkMode(hdc,TRANSPARENT);
  wsprintf(scoreString,"%d",score);
  wsprintf(lineString,"%d",lines);
  SetTextColor(hdc,RGB(255,255,255));
  TextOut(hdc,300,450,"Score:",lstrlen("Score:"));
	TextOut(hdc,310,470,scoreString,lstrlen(scoreString));
  TextOut(hdc,300,500,"Lines:",lstrlen("Lines:"));
	TextOut(hdc,310,520,lineString,lstrlen(lineString));
  SetTextColor(hdc,RGB(0,0,0));
  TextOut(hdc,301,451,"Score:",lstrlen("Score:"));
	TextOut(hdc,311,471,scoreString,lstrlen(scoreString));
  TextOut(hdc,301,501,"Lines:",lstrlen("Lines:"));    
	TextOut(hdc,311,521,lineString,lstrlen(lineString));
}

void drawInfo(HDC hdc) {
	static int hold;
  SetBkMode(hdc,TRANSPARENT);
  SetTextColor(hdc,RGB(255,255,255));
  TextOut(hdc,10,10,"Groovy Tetris",13);
  TextOut(hdc,12,32,"(c) 2000, KFS",13);
  SetTextColor(hdc,RGB(0,0,0));
  TextOut(hdc,11,11,"Groovy Tetris",13);
  TextOut(hdc,11,31,"(c) 2000, KFS",13);
  SetBkMode(hdc,OPAQUE);

	//smileyFace
	SelectObject(hdc,barBlack);
	Ellipse(memdc,168,198,272,302);
	SelectObject(hdc,smileyYellow);
	Ellipse(memdc,170,200,270,300);
	SelectObject(hdc,barBlack);
	if (hold || rand()%70 == 7) {
		if (hold) hold--; else hold=5;
		Rectangle(memdc,190,242,205,244);
		Rectangle(memdc,235,242,250,244);
	} else {
		Ellipse(memdc,190,235,205,250);
		Ellipse(memdc,235,235,250,250);
	}
	Arc(memdc,180,210,260,290,180,260,260,290);
}

void drawNext(HDC hdc) {
	int x,y;
  SetBkMode(hdc,TRANSPARENT);
  SetTextColor(hdc,RGB(255,255,255));
  TextOut(hdc,300,340,"Next:",lstrlen("Next:"));
  SetTextColor(hdc,RGB(0,0,0));
  TextOut(hdc,301,341,"Next:",lstrlen("Next:"));
	SetBkMode(hdc,OPAQUE);
	if (current != -1) {
		SelectObject(hdc,colors[nextPiece]);
    for (x=0;x<4;x++) for (y=0;y<4;y++) {
      if (data[nextPiece][0][y*4+x] != 0)
        RoundRect(hdc,x*25+300,y*-25+440,x*25+18+300,y*-25+18+440,7,7);
		}
	}
}

void drawBlocks(HDC hdc) {
	int x,y;

	SelectObject(hdc,barBlack);
	RoundRect(hdc,32,20,276,24,7,7);

  //for each x/y of the grid, paint what's there... then paint our block in its position?
  for (x=0;x<10;x++) for (y=0;y<20;y++) {
    if (grid[x][y] > -1) {
      SelectObject(hdc,colors[grid[x][y]]);
      RoundRect(hdc,x*25+25+7,y*25+25+7,x*25+25+25,y*25+25+25,7,7);
    }
  }
  if (current != -1) {
    SelectObject(hdc,colors[current]);
    for (x=0;x<4;x++) for (y=0;y<4;y++) {
      if (data[current][orientation][y*4+x] != 0)
        RoundRect(hdc,(posx+x)*25+25+7,(posy-y)*25+25+7,(posx+x)*25+25+25,(posy-y)*25+25+25,7,7);
    }
  }
}

void drawPaused(HDC hdc) {
  SetBkMode(hdc,TRANSPARENT);
  SetTextColor(hdc,RGB(255,255,255));
  TextOut(hdc,170,250,"-- Paused --",12);
  SetTextColor(hdc,RGB(0,0,0));
  TextOut(hdc,171,251,"-- Paused --",12);
  SetBkMode(hdc,OPAQUE);
}


void blit(){
	HWND ActiveWindow;
	HDC hdc,Context;
	RECT Dest;
	HBITMAP DefaultBitmap;
  if((ActiveWindow = GetActiveWindow()) == NULL)  return;
	hdc = GetDC(ActiveWindow);
	if (memdc == NULL) {
			memdc=CreateCompatibleDC(hdc);
			SelectObject(memdc,CreateFont(20,10,0,0,1000,0,0,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,PROOF_QUALITY,FF_SCRIPT,NULL));
			SelectObject(memdc,CreateCompatibleBitmap(hdc,W_WIDTH,W_HEIGHT));
			p_update();
	}
	GetClientRect(ActiveWindow,&Dest);
	Context = CreateCompatibleDC(0);
	DefaultBitmap = (HBITMAP__ *)SelectObject(Context,hBM);
	BitBlt(memdc,0,0,Dest.right,Dest.bottom,Context,0,0,SRCCOPY);
	SelectObject(Context,DefaultBitmap);
	DeleteDC(Context);
	switch (mode) {
		case INFO: 
			drawInfo(memdc);
			drawScore(memdc);
			break;
		case OVER:
			drawBlocks(memdc);
			drawScore(memdc);
			break;
		case RUNNING:
			if (drawNextPiece) drawNext(memdc);
			drawBlocks(memdc);
			drawScore(memdc);
			break;
		case PAUSED:
			drawPaused(memdc);
			drawScore(memdc);
			break;
		default:
			//error!?
			break;
	}
	BitBlt(hdc,0,0,Dest.right,Dest.bottom,memdc,0,0,SRCCOPY);
	DeleteObject(DefaultBitmap);
	ReleaseDC(ActiveWindow,hdc);
}

void endGame() {
  mode=INFO;
  paused=false;
  EnableMenuItem(menu,IDM_START,MF_ENABLED);
  EnableMenuItem(menu,IDM_STOP,MF_GRAYED);
  EnableMenuItem(menu,IDM_PAUSE,MF_GRAYED);
  gameOn=false;
}

int isHit(int myposx, int myposy, const short* tempblock) {
  int x,y;
  for (x=0;x<4;x++) for (y=0;y<4;y++) {
    if (tempblock[x+y*4] != 0) {
      //if this space exists in the grid, or is outside the left/right/bottom bounds... bip
      if (myposx+x < 0 || myposx+x > 9 || myposy-y < 0) return 1;
      if (grid[myposx+x][myposy-y] != -1) return 1;
    }
  }
  return 0;
}

void setBlock(int myposx, int myposy, int myorientation,int mycurrent) {
  int x,y,clear,numclear,suby;
  //KillTimer(hwnd,1);
  for (x=0;x<4;x++) for (y=0;y<4;y++) {
    if(data[mycurrent][myorientation][x+y*4] != 0) {
      grid[myposx+x][myposy-y] = current;
    }
  }
	x=nextPiece;
  nextPiece=(short)(rand()%7); posx=(short)3; posy=(short)22; orientation=(short)0;
	current=x;
  if (isHit(posx,posy,data[current][orientation])) {
    endGame();
    return;
  }
  /*test to see if we can clear some lines :)*/
  numclear=0;
  for (y=0;y<21;y++) {
    clear=1;
    for (x=0;x<10;x++) if (grid[x][y] == -1) clear=0;
    if (clear) {
      numclear++;
      score+=100*numclear;
      lines+=1;
      for (suby=y+1;suby<21;suby++) for (x=0;x<10;x++) grid[x][suby-1]=grid[x][suby];
      y--;
      speed=(int)(((float)lines/(float)125)*BASE_SPEED);
      if (BASE_SPEED-speed < 100) speed=BASE_SPEED-100;
    }
  }
  clear=1;
  for (x=0;x<10;x++) if (grid[x][21] != -1) clear=0;
  if (clear!=1) endGame();
  //SetTimer(hwnd,1,BASE_SPEED-speed,NULL);
  /*test to see if the game is over*/
}

void testBounds(short changeType, int amount) {
  //if we would hit something, and it's due to ydif, stick us there.
  //hitting something INCLUDES the bottom of the screen, BTW, just in case you
  //were wondering, this is where that check happens. Neener neener. :)
  //if we're rotating or xdiffing or ydiffing and not hitting anything, make it so.
  int i,mycurrent,myposx,myposy,myorientation;
  short tempblock[16];
  mycurrent=current; myposx=posx; myposy=posy; myorientation=orientation;
  if (changeType == CHANGEY) {
    for (i=0;i<16;i++) tempblock[i]=data[mycurrent][myorientation][i];
    if (isHit(myposx,myposy-amount,tempblock) == 1) {
      //stick us there
      setBlock(myposx,myposy,myorientation,mycurrent);
    } else {
      posy=posy-((short)amount);
    }
  } else if (changeType == CHANGEX) {
    for (i=0;i<16;i++) tempblock[i]=data[current][orientation][i];
    if (isHit(myposx-amount,myposy,tempblock) != 1) posx=posx-(short)amount;
  } else { //changeType == CHANGEROT
    for (i=0;i<16;i++) tempblock[i]=data[current][(orientation+1)%4][i];
    if (isHit(myposx,myposy,tempblock) != 1) orientation=(short)((orientation+1)%4);
  }
}

void CALLBACK plink(HWND hwnd, UINT uiMsg, UINT idEvent, DWORD dwTime) {
	while (true) {
		Sleep(BASE_SPEED-lines*10);
		if (!paused)  testBounds(CHANGEY,1);
	}
}

LRESULT CALLBACK WndProc(HWND hwnd,UINT iMessage,	WPARAM wParam,LPARAM lParam){
	int x,y;
	switch(iMessage){
		case WM_TIMER:
			MessageBeep(-1);
			testBounds(CHANGEY,1);
			return 0;
		case WM_KEYDOWN:
      switch (wParam) {
        case VK_UP: testBounds(CHANGEY,1); break;
        case VK_LEFT: testBounds(CHANGEX,1); break;
        case VK_RIGHT: testBounds(CHANGEX,-1); break;
        case VK_DOWN: testBounds(CHANGEROT,1); break;
        default: break;
      }
			return 0;
    case WM_COMMAND:
      switch(LOWORD(wParam)) {
        case IDM_START:
          mode=RUNNING;
          EnableMenuItem(menu,IDM_START,MF_GRAYED);
          EnableMenuItem(menu,IDM_STOP,MF_ENABLED);
          EnableMenuItem(menu,IDM_PAUSE,MF_ENABLED);
          gameOn=true;
          srand(time(NULL));
          lines=0; score=0; speed=0;
          for (x=0;x<10;x++) for (y=0;y<22;y++) grid[x][y]=-1;
          //start the tick thread
          current=(short)(rand()%7); posx=(short)3; posy=(short)22; orientation=(short)0;
					//SetWindowText (hwnd, title);
      //    SetTimer(hwnd,1,BASE_SPEED,NULL);
					break;
        case IDM_STOP:
        //  KillTimer(hwnd,1);
          mode=INFO;
          paused=false;
          EnableMenuItem(menu,IDM_START,MF_ENABLED);
          EnableMenuItem(menu,IDM_STOP,MF_GRAYED);
          EnableMenuItem(menu,IDM_PAUSE,MF_GRAYED);
          gameOn=false;
          break;
        case IDM_EXIT:
          PostQuitMessage(0);
          break;
        case IDM_PAUSE:
          paused=!paused;
          mode = (paused)?PAUSED:RUNNING;
          //if (paused) KillTimer(hwnd,1); else SetTimer(hwnd,1,BASE_SPEED-speed,NULL);
          break;
        case IDM_MINIMIZE:
          ShowWindow(hwnd, SW_SHOWMINIMIZED);
          break;
      }
      return 0;
    case WM_SIZE:
      /*resized: wParam -- SIZE_; RESTORED, MAXIMIZED, MINIMIZED, ...*/
      /*LOWORD(lParam) width, HIWORD(lParam) height*/
      switch (wParam) {
        case SIZE_MAXIMIZED:
        case SIZE_RESTORED:
          //W_HEIGHT=HIWORD(lParam);
          //W_WIDTH=LOWORD(lParam);
          break;
        case SIZE_MINIMIZED:
          if (mode==RUNNING) {
            mode=PAUSED; paused=true;
          }
          break;
      }
      return 0;
    case WM_PAINT: /* process a repaint request */
			blit();
      return 0;
		case WM_CREATE:
			menu=GetMenu(hwnd);
			/* make a bunch of brushes for to paint rectangles */
      colors[EL]=CreateSolidBrush(RGB(50,50,150));
      colors[ELB]=CreateSolidBrush(RGB(150,50,50));
      colors[S]=CreateSolidBrush(RGB(100,50,100));
      colors[Z]=CreateSolidBrush(RGB(50,150,50));
      colors[BLOCK]=CreateSolidBrush(RGB(200,50,25));
      colors[BAR]=CreateSolidBrush(RGB(200,200,200));
      colors[TEE]=CreateSolidBrush(RGB(0,0,0));
			smileyYellow=CreateSolidBrush(RGB(255,255,0));
			barBlack=CreateSolidBrush(RGB(30,30,30));

      /*variable initialization*/
      paused=false; gameOn=false;
      flameCount=0;
      mode=INFO;

			CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FlameThread,(LPVOID)hwnd,0,&TidFlame);
			CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)plink,(LPVOID)hwnd,0,&TidPlink);

			return 0;
		case WM_DESTROY:
			PostQuitMessage(0);
			return 0;
		default:
			return DefWindowProc(hwnd,iMessage,wParam,lParam);
	}
}


int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance, LPSTR lpszCmdParam,int nCmdShow){
	WNDCLASS WndClass;
	HACCEL hAccel;
	MSG msg;
	BOOL running = TRUE;

	WndClass.style = CS_HREDRAW|CS_VREDRAW|CS_OWNDC;
	WndClass.lpfnWndProc = WndProc;
	WndClass.cbClsExtra = 0;
	WndClass.cbWndExtra = 0;
	WndClass.hbrBackground = (HBRUSH__ *) GetStockObject(BLACK_BRUSH);
	WndClass.hIcon = LoadIcon(hInstance,NULL);
	WndClass.hCursor = LoadCursor(NULL,IDC_ARROW);
	WndClass.hInstance = (HINSTANCE__ *) hInstance;
	WndClass.lpszClassName = szAppName;
	WndClass.lpszMenuName = "Tetris";

	hAccel = LoadAccelerators(hInstance,"Tetris");

	RegisterClass(&WndClass);

	hwnd = CreateWindow(szAppName,szAppName,
		//WS_CAPTION|WS_MINIMIZEBOX|WS_SYSMENU,
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,CW_USEDEFAULT,W_WIDTH,W_HEIGHT,
		HWND_DESKTOP,0,hInstance,0);
	ShowWindow(hwnd,nCmdShow);

	init_double_buffer();

	while (running = GetMessage(&msg, NULL, 0, 0)) {
		//if (running == -1);
		if (!TranslateAccelerator(hwnd,hAccel,&msg)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	return msg.wParam;
}
