// -std=c++14 -lgdi32 -lOpengl32 -ld3d11 -ldxgi

#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <wrl/client.h>

#include <d3d11_4.h>
#include <dxgi1_6.h>
#include <d3dcompiler.h>

#include <DirectXMath.h>

#include <gl/gl.h>

#include <cmath>
#include <vector>
#include <ctime>
#include <string>
#include <iostream>

using namespace std;

namespace D3D11 {
using namespace std;
using namespace Microsoft::WRL;
using namespace DirectX;

#ifdef assert
#undef assert
#endif
#define assert(expr) (!(bool) (expr) ? __my_assert_report(#expr, __FILE__, __LINE__) : (void) 0)
#define CHK(expr) check_error((expr))

struct Size2D {
    int w, h;
};

namespace
{
void break_or_exit() {
    if (IsDebuggerPresent()) __debugbreak();
    else exit(-1);
}

void __my_assert_report(const char* expr, const char* file, int line) {
	cerr << "Assertion failed: " << expr << " in file " << file << ", line " << line << "." << endl;
    break_or_exit();
}

wstring get_error_message(DWORD error_code) {
    wchar_t* buf = nullptr;
    FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
      error_code, 0, (LPWSTR) &buf, 0, nullptr);
    if (!buf) return L"Error "+to_wstring(error_code);
	auto msg=L"Error "+to_wstring(error_code)+L": "+buf;
    LocalFree(buf);
    return msg;
}

void report_win32_error() {
    if (GetLastError() != ERROR_SUCCESS) {
        auto msg = get_error_message(GetLastError());
        wcerr << msg << endl;
        break_or_exit();
    }
}

void report_hresult(HRESULT hr) {
    if (FAILED(hr)) {
        auto msg = get_error_message(HRESULT_CODE(hr));
        wcerr << msg << endl;
        break_or_exit();
    }
}

template<typename T> void check_error(T) { static_assert(0, "Unknown result type"); }
template<> void check_error(HRESULT hr) { report_hresult(hr); }
template<> void check_error(BOOL ok) {
    if (!ok) report_win32_error();
}
template<> void check_error(HANDLE handle) {
    if (handle == INVALID_HANDLE_VALUE) report_win32_error();
}

vector<uint8_t> read_file_bin(const wchar_t* filename) {
    auto file
      = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
    CHK(file);
    LARGE_INTEGER size;
    CHK(GetFileSizeEx(file, &size));
    vector<uint8_t> buffer(size.QuadPart, '\0');
    DWORD size_read;
    CHK((BOOL) (ReadFile(file, buffer.data(), size.QuadPart, &size_read, nullptr) && size_read == size.QuadPart));
    return buffer;
}

template<typename T, size_t N> constexpr size_t array_size(T (&)[N]) { return N; }

struct DrawCmd {
	int x,y,w,h; 
	float col[3];
};
vector<DrawCmd> draw_cmds;

HWND hwnd;
ComPtr<IDXGIFactory4> dxgi_factory;
ComPtr<IDXGIAdapter4> dxgi_adapter;
ComPtr<IDXGISwapChain4> swap_chain;
ComPtr<ID3D11Device5> device;
ComPtr<ID3D11DeviceContext4> device_context;
Size2D d3d11_buffer_size;
vector<uint8_t> vertex_shader_bin, pixel_shader_bin;
ComPtr<ID3D11VertexShader> vertex_shader;
ComPtr<ID3D11PixelShader> pixel_shader;
ComPtr<ID3D11InputLayout> input_layout;
ComPtr<ID3D11Buffer> vertex_buffer;
ComPtr<ID3D11Buffer> constant_buffer1;

struct VertexData {
    XMINT2 position;
    XMFLOAT3 color;
};
struct ConstantBuffer1 {
    XMINT2 size;
};

void load_shader() {
    vertex_shader_bin = read_file_bin(L".\\build\\1.vs.cso");
    CHK(device->CreateVertexShader(vertex_shader_bin.data(), vertex_shader_bin.size(), nullptr, &vertex_shader));
    pixel_shader_bin = read_file_bin(L".\\build\\1.ps.cso");
    CHK(device->CreatePixelShader(pixel_shader_bin.data(), pixel_shader_bin.size(), nullptr, &pixel_shader));
}
Size2D get_screen_size() { return {GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)}; }
Size2D get_window_size() {
    RECT r;
    GetClientRect(hwnd, &r);
    return {r.right - r.left, r.bottom - r.top};
}

void init_D3D11() {
    d3d11_buffer_size = get_screen_size();
    CHK(CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG, IID_PPV_ARGS(&dxgi_factory)));
    {
        ComPtr<IDXGIAdapter1> dxgi_adapter_tmp;
        CHK(dxgi_factory->EnumAdapters1(0, &dxgi_adapter_tmp));
        CHK(dxgi_adapter_tmp.As(&dxgi_adapter));
        DXGI_ADAPTER_DESC desc;
        CHK(dxgi_adapter->GetDesc(&desc));
        wcerr << L"Using adapter: " << desc.Description << endl;
    }
    {
        ComPtr<ID3D11Device> device_tmp;
        CHK(D3D11CreateDevice(dxgi_adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_DEBUG, nullptr,
          0, D3D11_SDK_VERSION, &device_tmp, nullptr, nullptr));
        CHK(device_tmp.As(&device));
        ComPtr<ID3D11DeviceContext3> device_context_tmp;
        device->GetImmediateContext3(&device_context_tmp);
        CHK(device_context_tmp.As(&device_context));
    }
    {
        DXGI_SWAP_CHAIN_DESC1 desc;
        memset(&desc, 0, sizeof(desc));
		auto w=d3d11_buffer_size.w, h=d3d11_buffer_size.h;
        desc.Width = w, desc.Height = h;
        desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        desc.SampleDesc = {1,0};
        desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        desc.BufferCount = 2;
        desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
        desc.Scaling = DXGI_SCALING_NONE;
        desc.Flags = 0;
        ComPtr<IDXGISwapChain1> swap_chain_tmp;
        CHK(dxgi_factory->CreateSwapChainForHwnd(device.Get(), hwnd, &desc, nullptr, nullptr, &swap_chain_tmp));
        CHK(swap_chain_tmp.As(&swap_chain));
    }
    load_shader();
    {
        D3D11_INPUT_ELEMENT_DESC layout[] {
          {"POSITION", 0, DXGI_FORMAT_R32G32_SINT,     0, offsetof(VertexData, position), D3D11_INPUT_PER_VERTEX_DATA, 0},
          {"COLOR",    0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(VertexData, color),    D3D11_INPUT_PER_VERTEX_DATA, 0}
        };
        CHK(device->CreateInputLayout(
          layout, array_size(layout), vertex_shader_bin.data(), vertex_shader_bin.size(), &input_layout));
    }
    {
        D3D11_BUFFER_DESC desc;
        memset(&desc, 0, sizeof(desc));
        desc.ByteWidth = 1048576;
        desc.Usage = D3D11_USAGE_DYNAMIC;
        desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
        desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
        CHK(device->CreateBuffer(&desc, nullptr, &vertex_buffer));
    }
    {
        D3D11_BUFFER_DESC desc;
        memset(&desc, 0, sizeof(desc));
        desc.ByteWidth = 64;
        desc.Usage = D3D11_USAGE_DYNAMIC;
        desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
        desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
        CHK(device->CreateBuffer(&desc, nullptr, &constant_buffer1));
    }
}

void render() {
    ComPtr<ID3D11Texture2D> back_buffer;
    swap_chain->GetBuffer(0, IID_PPV_ARGS(&back_buffer));
    ComPtr<ID3D11RenderTargetView> back_buffer_rtv;
    CHK(device->CreateRenderTargetView(back_buffer.Get(), nullptr, &back_buffer_rtv));
    device_context->OMSetRenderTargets(1, back_buffer_rtv.GetAddressOf(), nullptr);

    D3D11_VIEWPORT viewport;
    viewport.TopLeftX = 0, viewport.TopLeftY = 0;
    viewport.Width = d3d11_buffer_size.w, viewport.Height = d3d11_buffer_size.h;
    viewport.MinDepth = 0.0f, viewport.MaxDepth = 1.0f;
    device_context->RSSetViewports(1, &viewport);

    float bgr_color[] {0.92f,0.92f,0.92f, 1.0f};
    device_context->ClearRenderTargetView(back_buffer_rtv.Get(), bgr_color);

    device_context->VSSetShader(vertex_shader.Get(), nullptr, 0);
    device_context->PSSetShader(pixel_shader.Get(), nullptr, 0);
    device_context->IASetInputLayout(input_layout.Get());
    device_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    {
        D3D11_MAPPED_SUBRESOURCE map_info;
        CHK(device_context->Map(constant_buffer1.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &map_info));
        // int h = get_window_size().h, w = get_window_size().w;
		int h = d3d11_buffer_size.h, w = d3d11_buffer_size.w;
        ConstantBuffer1 cb1 {};
		cb1.size = {w, h};
        memcpy(map_info.pData, &cb1, sizeof(cb1));
        device_context->Unmap(constant_buffer1.Get(), 0);
        device_context->VSSetConstantBuffers(0, 1, constant_buffer1.GetAddressOf());
    }

    {
		vector<VertexData> vertices;
		for (auto& cmd : draw_cmds) {
			VertexData v1, v2, v3, v4;
			v1.position = {cmd.x, cmd.y};
			v2.position = {cmd.x+cmd.w, cmd.y};
			v3.position = {cmd.x+cmd.w, cmd.y+cmd.h};
			v4.position = {cmd.x, cmd.y+cmd.h};
			v1.color={cmd.col[0], cmd.col[1], cmd.col[2]};
			v2.color={cmd.col[0], cmd.col[1], cmd.col[2]};
			v3.color={cmd.col[0], cmd.col[1], cmd.col[2]};
			v4.color={cmd.col[0], cmd.col[1], cmd.col[2]};
			vertices.push_back(v1);
			vertices.push_back(v2);
			vertices.push_back(v3);
			vertices.push_back(v4);
			vertices.push_back(v1);
			vertices.push_back(v3);
		}
		if(vertices.size()){
			D3D11_MAPPED_SUBRESOURCE map_info;
			CHK(device_context->Map(vertex_buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &map_info));
			memcpy(map_info.pData, vertices.data(), sizeof(vertices[0])*vertices.size());
			device_context->Unmap(vertex_buffer.Get(), 0);
			UINT stride = sizeof(VertexData), offset = 0;
			device_context->IASetVertexBuffers(0, 1, vertex_buffer.GetAddressOf(), &stride, &offset);
			device_context->Draw(vertices.size(), 0);
		}
    }

    CHK(swap_chain->Present(0, 0));
}

}  // namespace
}

void EnableOpenGL(HWND hWnd, HDC *hDC, HGLRC *hRC) {
	PIXELFORMATDESCRIPTOR pfd; int iFormat;
	*hDC = GetDC(hWnd);
	ZeroMemory(&pfd, sizeof(pfd));
	pfd.nSize = sizeof(pfd);
	pfd.nVersion = 1;
	pfd.dwFlags = PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER;
	pfd.iPixelType = PFD_TYPE_RGBA;
	pfd.cColorBits = 24;
	pfd.cDepthBits = 16;
	pfd.iLayerType = PFD_MAIN_PLANE;
	iFormat = ChoosePixelFormat(*hDC, &pfd);
	SetPixelFormat(*hDC, iFormat, &pfd);
	*hRC = wglCreateContext(*hDC);
	wglMakeCurrent(*hDC, *hRC);
}
void DisableOpenGL(HWND hWnd, HDC hDC, HGLRC hRC) {
	wglMakeCurrent(NULL, NULL);
	wglDeleteContext(hRC);
	ReleaseDC(hWnd, hDC);
}
int Mix(int ca, int cb, double rt) {
	int Ar = (ca>>16);
	int Ag = ((ca>>8)&((1<<8)-1));
	int Ab = (ca&((1<<8)-1));
	int Br = (cb>>16);
	int Bg = ((cb>>8)&((1<<8)-1));
	int Bb = (cb&((1<<8)-1));
	int R = int(floor(rt*double(Ar)+(1.0-rt)*double(Br)));
	int G = int(floor(rt*double(Ag)+(1.0-rt)*double(Bg)));
	int B = int(floor(rt*double(Ab)+(1.0-rt)*double(Bb)));
	return (max(0,min(255,R))<<16)+(max(0,min(255,G))<<8)+max(0,min(255,B));
}
int PS = 800;
struct ClassPiece {
	int B[10][10], LX, LY, clr, SDel, FstX, FstY;
	ClassPiece() {
		LX=LY=clr=SDel=0; FstX=FstY=-1;
		for (int i=0; i<=9; ++i) {
			for (int j=0; j<=9; ++j) B[i][j]=0;
		}
	}
	ClassPiece(int _LX, int _LY, int _clr, int _SDel, vector<vector<int> > _B) {
		LX=_LX; LY=_LY; clr=_clr; SDel=_SDel; FstX=FstY=-1;
		for (int i=0; i<=9; ++i) {
			for (int j=0; j<=9; ++j) B[i][j]=0;
		}
		for (int i=0; i<_LX; ++i) {
			for (int j=0; j<_LY; ++j) {
				B[i][j] = _B[_LX-i-1][j];
				if ((B[i][j])&&(FstX<0)) {
					FstX=i; FstY=j;
				}
			}
		}
	}
} Piece[55][4];
vector<pair<int,int> > Kick[55][4][4];
bool CheckKey(char ch) {
	return bool(GetAsyncKeyState(ch)&0x8000);
}
struct Board {
	int B[111][111], C[111][111], LX, LY, DX;
	double KX, KY; int Pid, Dir;
	constexpr static char KEY_LEFT = VK_LEFT;
	constexpr static char KEY_RIGHT = VK_RIGHT;
	constexpr static char KEY_DOWN = VK_DOWN;
	constexpr static char KEY_CW = 'x';
	constexpr static char KEY_CCW = 'z';
	constexpr static char KEY_180 = 'a';
	constexpr static char KEY_HARD = ' ';
	vector<char> KeyList = {KEY_LEFT,KEY_RIGHT,KEY_DOWN,KEY_CW,KEY_CCW,KEY_180,KEY_HARD};
	int KeySta[256];
	vector<int> pressed_keys;
	uint64_t key_down[256];
	Board(int _LX, int _LY, int _DX, double _KX, double _KY) {
		LX=_LX; LY=_LY; DX=_DX;
		KX=_KX; KY=_KY;
		for (int i=0; i<111; ++i) {
			for (int j=0; j<111; ++j) B[i][j]=C[i][j]=0;
		}
		for (int i=0; i<256; ++i) KeySta[i]=0;
	}
	void Plot(double x, double y, int clr) {
		x+=KX; y+=KY;
		int B=clr&255; clr>>=8;
		int G=clr&255; clr>>=8;
		int R = clr&255;
		double px = 1.0-x/double(PS);
		double py = y/double(PS);
		px = px*1.8-0.9;
		py = py*1.8-0.9;
		glColor3f(min(1.0,max(0.0,double(R)/255.0)), min(1.0,max(0.0,double(G)/255.0)), min(1.0,max(0.0,double(B)/255.0)));
		glVertex2f(py, px);
	}
	void Poly(double x, double y, double r, int n, int clr, double th=0.0) {
		glBegin(GL_POLYGON);
		for (int i=0; i<n; ++i) {
			Plot(x-r*cos(th), y+r*sin(th), clr);
			th += 2.0*acos(-1.0)/double(n);
		}
		glEnd();
	}
	void Square(double x, double y, double d, int clr) {
		glBegin(GL_POLYGON);
		Plot(x, y, clr);
		Plot(x+d, y, clr);
		Plot(x+d, y+d, clr);
		Plot(x, y+d, clr);
		glEnd();
	}
	bool CheckActive() {
		for (int i=1; i<=LX; ++i) {
			for (int j=1; j<=LY; ++j) {
				if (C[i][j]) return true;
			}
		}
		return false;
	}
	bool CheckPlace(int x, int y) {
		if ((min(x,y)<1)||((x>LX)||(y>LY))) return true;
		return ((B[x][y])&&(!C[x][y]));
	}
	bool Spawn(int _Pid, int _Dir) {
		ClassPiece P = Piece[_Pid][_Dir];
		if (LY<P.LY) return false;
		int SX=DX-1+P.SDel, SY=(LY-P.LY)/2+1;
		SY = max(1,min(LY-P.LY+1,SY));
		SX = max(1,min(LX,SX));
		for (int i=0; i<P.LX; ++i) {
			for (int j=0; j<P.LY; ++j) {
				if ((CheckPlace(SX+i,SY+j))&&(P.B[i][j])) return false;
			}
		}
		for (int i=0; i<P.LX; ++i) {
			for (int j=0; j<P.LY; ++j) {
				if (P.B[i][j]) {
					C[SX+i][SY+j]=1; B[SX+i][SY+j]=_Pid;
				}
			}
		}
		Pid=_Pid; Dir=_Dir;
		return true;
	}
	bool CheckDown() {
		for (int i=1; i<=LX; ++i) {
			for (int j=1; j<=LY; ++j) {
				if ((C[i][j])&&(CheckPlace(i-1,j))) return false;
			}
		}
		return true;
	}
	void MoveDown() {
		if (!CheckDown()) return;
		for (int i=1; i<LX; ++i) {
			for (int j=1; j<=LY; ++j) {
				if (C[i+1][j]) {
					B[i][j]=B[i+1][j]; C[i][j]=1;
					B[i+1][j]=C[i+1][j]=0;
				}
			}
		}
	}
	bool CheckLeft() {
		for (int i=1; i<=LX; ++i) {
			for (int j=1; j<=LY; ++j) {
				if ((C[i][j])&&(CheckPlace(i,j-1))) return false;
			}
		}
		return true;
	}
	bool MoveLeft() {
		if (!CheckLeft()) return false;
		for (int i=1; i<=LX; ++i) {
			for (int j=1; j<=LY; ++j) {
				if (C[i][j+1]) {
					B[i][j]=B[i][j+1]; C[i][j]=1;
					B[i][j+1]=C[i][j+1]=0;
				}
			}
		}
		return true;
	}
	bool CheckRight() {
		for (int i=1; i<=LX; ++i) {
			for (int j=1; j<=LY; ++j) {
				if ((C[i][j])&&(CheckPlace(i,j+1))) return false;
			}
		}
		return true;
	}
	bool MoveRight() {
		if (!CheckRight()) return false;
		for (int i=1; i<=LX; ++i) {
			for (int j=LY; j; --j) {
				if (C[i][j-1]) {
					B[i][j]=B[i][j-1]; C[i][j]=1;
					B[i][j-1]=C[i][j-1]=0;
				}
			}
		}
		return true;
	}
	bool Rotate(int NewDir) {
		if ((NewDir<0)||(NewDir>3)||(NewDir==Dir)||(!Pid)) return false;
		int FstX=0, FstY=0;
		for (int i=1; i<=LX; ++i) {
			for (int j=1; j<=LY; ++j) {
				if (C[i][j]) {
					FstX=i; FstY=j; break;
				}
			}
			if (FstX) break;
		}
		if (!FstX) return false;
		int SX=FstX-Piece[Pid][Dir].FstX, SY=FstY-Piece[Pid][Dir].FstY;
		ClassPiece P = Piece[Pid][NewDir];
		bool flag = false;
		for (pair<int,int> KT : Kick[Pid][Dir][NewDir]) {
			SX+=KT.second; SY+=KT.first;
			bool ok = true;
			for (int i=0; i<P.LX; ++i) {
				for (int j=0; j<P.LY; ++j) {
					if ((P.B[i][j])&&(CheckPlace(SX+i,SY+j))) {
						ok=false; break;
					}
				}
				if (!ok) break;
			}
			if (ok) {
				flag=true; break;
			}
			else {
				SX-=KT.second; SY-=KT.first;
			}
		}
		if (!flag) return false;
		for (int i=1; i<=LX; ++i) {
			for (int j=1; j<=LY; ++j) {
				if (C[i][j]) B[i][j]=C[i][j]=0;
			}
		}
		for (int i=0; i<P.LX; ++i) {
			for (int j=0; j<P.LY; ++j) {
				if (P.B[i][j]) {
					B[SX+i][SY+j]=Pid; C[SX+i][SY+j]=1;
				}
			}
		}
		Dir = NewDir;
		return true;
	}
	bool SpawnPiece() {
		return Spawn(2,0);
	}
	void Lock() {
		for (int i=1; i<=LX; ++i) {
			for (int j=1; j<=LY; ++j) C[i][j]=0;
		}
	}
	void HardDrop() {
		while (CheckDown()) MoveDown();
		Lock();
	}
	void Square1(int x,int y,int d,int clr){
		float b=(clr&255)/256.; clr>>=8;
		float g=(clr&255)/256.; clr>>=8;
		float r=(clr&255)/256.;
		D3D11::draw_cmds.push_back({x,y,d,d, {r,g,b}});
	}
	void Paint() {
		D3D11::draw_cmds.clear();
		double sz = min(400.0/double(LY),800.0/double(DX))*0.8;
		for (int i=1; i<=DX; ++i) {
			for (int j=1; j<=LY; ++j) {
				int clr = (B[i][j])?Piece[B[i][j]][0].clr:0xFFFFFF;
				if ((!C[i][j])&&(B[i][j])) clr=Mix(0x000000,clr,0.1);
				// Square(sz*double(DX-i),sz*double(j-1),sz,clr);
				Square1(j*50,(DX-i)*50,50,clr);
			}
		}
	}
	vector<char> GetKey() {
		vector<char> res;
		for (char ch : KeyList) {
			bool nw = CheckKey(ch);
			if ((nw)&&(!KeySta[ch])) res.push_back(ch);
			KeySta[ch] = nw;
		}
		return res;
	}
	void Frame() {
		if (!CheckActive()) {
			SpawnPiece();
		}
		if (CheckActive()) {
			for (char ch : pressed_keys) {
				if (ch==KEY_LEFT) MoveLeft();
				if (ch==KEY_RIGHT) MoveRight();
				if (ch==KEY_CCW) Rotate((Dir+1)&3);
				if (ch==KEY_CW) Rotate((Dir+3)&3);
				if (ch==KEY_180) Rotate((Dir+2)&3);
				if (ch==KEY_DOWN) MoveDown();
				if (ch==KEY_HARD) HardDrop();
			}
		}
        pressed_keys.clear();
		Paint();
	}
} P1(40,10,22,0.0,0.0);


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	auto board=(Board*)GetWindowLongPtr(hWnd,GWLP_USERDATA);
	switch (message) {
	case WM_CREATE:
		return 0;
	case WM_CLOSE:
		PostQuitMessage (0);
		return 0;
	case WM_DESTROY:
		return 0;
	case WM_KEYDOWN:
		if (board){
			auto key=wParam;
			if(!board->key_down[key]||clock()-board->key_down[key]>CLOCKS_PER_SEC*0.03){
				board->key_down[key]=clock();
				if (key==VK_LEFT || key==VK_RIGHT || key==VK_DOWN || key==VK_UP) PostMessage(hWnd,WM_CHAR,key,0);
			}
		}
		return 0;
	case WM_KEYUP:
		if(board){ auto key=wParam; board->key_down[key]=0; }
		return 0;
    case WM_CHAR:
		if (board) board->pressed_keys.push_back(wParam);
        return 0;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
}
void prepare_data(){
	// clang-format off
	Piece[1][0] = ClassPiece(4,4,0x66CCFF,-1,{{0,0,0,0},{1,1,1,1},{0,0,0,0},{0,0,0,0}});
	Piece[1][1] = ClassPiece(4,4,0x66CCFF,-1,{{0,0,1,0},{0,0,1,0},{0,0,1,0},{0,0,1,0}});
	Piece[1][2] = ClassPiece(4,4,0x66CCFF,-1,{{0,0,0,0},{0,0,0,0},{1,1,1,1},{0,0,0,0}});
	Piece[1][3] = ClassPiece(4,4,0x66CCFF,-1,{{0,1,0,0},{0,1,0,0},{0,1,0,0},{0,1,0,0}});
	Piece[2][0] = ClassPiece(3,3,0x0080FF,-1,{{1,0,0},{1,1,1},{0,0,0}});
	Piece[2][1] = ClassPiece(3,3,0x0080FF,-1,{{0,1,1},{0,1,0},{0,1,0}});
	Piece[2][2] = ClassPiece(3,3,0x0080FF,-1,{{0,0,0},{1,1,1},{0,0,1}});
	Piece[2][3] = ClassPiece(3,3,0x0080FF,-1,{{0,1,0},{0,1,0},{1,1,0}});
	for (int i=0; i<4; ++i) {
		for (int j=0; j<4; ++j) {
			Kick[1][0][3]={{0,0},{-2,0},{+1,0},{-2,-1},{+1,+2}};
			Kick[1][3][0]={{0,0},{+2,0},{-1,0},{+2,+1},{-1,-2}};
			Kick[1][3][2]={{0,0},{-1,0},{+2,0},{-1,+2},{+2,-1}};
			Kick[1][2][3]={{0,0},{+1,0},{-2,0},{+1,-2},{-2,+1}};
			Kick[1][2][1]={{0,0},{+2,0},{-1,0},{+2,+1},{-1,-2}};
			Kick[1][1][2]={{0,0},{-2,0},{+1,0},{-2,-1},{+1,+2}};
			Kick[1][1][0]={{0,0},{+1,0},{-2,0},{+1,-2},{-2,+1}};
			Kick[1][0][1]={{0,0},{-1,0},{+2,0},{-1,+2},{+2,-1}};
			Kick[1][0][2]={{0,0},{0,+1},{+1,+1},{-1,+1},{+1,0},{-1,0}};
			Kick[1][2][0]={{0,0},{0,-1},{-1,-1},{+1,-1},{-1,0},{+1,0}};
			Kick[1][1][3]={{0,0},{-1,0},{-1,+2},{-1,+1},{0,+2},{0,+1}};
			Kick[1][3][1]={{0,0},{+1,0},{+1,+2},{+1,+1},{0,+2},{0,+1}};
			for (int o=2; o<55; ++o) {
				Kick[o][0][3]={{0,0},{-1,0},{-1,+1},{0,-2},{-1,-2}};
				Kick[o][3][0]={{0,0},{+1,0},{+1,-1},{0,+2},{+1,+2}};
				Kick[o][3][2]={{0,0},{+1,0},{+1,-1},{0,+2},{+1,+2}};
				Kick[o][2][3]={{0,0},{-1,0},{-1,+1},{0,-2},{-1,-2}};
				Kick[o][2][1]={{0,0},{+1,0},{+1,+1},{0,-2},{+1,-2}};
				Kick[o][1][2]={{0,0},{-1,0},{-1,-1},{0,+2},{-1,+2}};
				Kick[o][1][0]={{0,0},{-1,0},{-1,-1},{0,+2},{-1,+2}};
				Kick[o][0][1]={{0,0},{+1,0},{+1,+1},{0,-2},{+1,-2}};
				Kick[o][0][2]={{0,0},{0,+1},{+1,+1},{-1,+1},{+1,0},{-1,0}};
				Kick[o][2][0]={{0,0},{0,-1},{-1,-1},{+1,-1},{-1,0},{+1,0}};
				Kick[o][1][3]={{0,0},{-1,0},{-1,+2},{-1,+1},{0,+2},{0,+1}};
				Kick[o][3][1]={{0,0},{+1,0},{+1,+2},{+1,+1},{0,+2},{0,+1}};
			}
		}
	}
    // clang-format on
}
int main() {
    SetProcessDPIAware();
	WNDCLASS wc;
	HWND hWnd;
	HDC hDC;
	HGLRC hRC;		
	MSG msg;
	BOOL bQuit = FALSE;
	wc.style = CS_OWNDC;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = GetModuleHandle(nullptr);
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = HBRUSH(GetStockObject (BLACK_BRUSH));
	wc.lpszMenuName = NULL;
	wc.lpszClassName = "GLSample";
	RegisterClass(&wc);
	//hWnd = GetConsoleWindow();
	hWnd = CreateWindow("GLSample", "Tetris", WS_OVERLAPPEDWINDOW, 0, 0, 1000, 1000, NULL, NULL, GetModuleHandle(nullptr), NULL);
	ShowWindow(hWnd, 1);
	SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)&P1);
	// EnableOpenGL(hWnd, &hDC, &hRC);
	D3D11::hwnd=hWnd;
	D3D11::init_D3D11();
	prepare_data();
	while (!bQuit) {
		while (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
			if (msg.message == WM_QUIT) bQuit=true;
			else {
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		{
			// glClearColor(0.92, 0.92, 0.92, 0);
			// glClear(GL_COLOR_BUFFER_BIT);
			// glPushMatrix();
			P1.Frame();
			D3D11::render();
			// glPopMatrix();
			// SwapBuffers(hDC);
			Sleep(16);
		}
	}
	// DisableOpenGL(hWnd, hDC, hRC);
	return 0;
}