#include "stdafx.h"

HINSTANCE hAppInstance = 0;
#pragma warning(disable:4459)
// ----
const TCHAR* ttitle = _T("Roll");
#ifdef _WIN64
#import "..\\vstx64.dll" raw_interfaces_only
#pragma comment(lib,"..\\vstx64.lib")
#else
#import "..\\vstx32.dll" raw_interfaces_only
#pragma comment(lib,"..\\vstx32.lib")
#endif
#include "..\\vstx.h"

#include "..\\mt\\rw.hpp"
#include "..\\xml\\xml3all.h"
// ----
bool Ending = false;
int SR = 44100;
int BS = 4410;
int BR = 32;
#include "..\\test\\wave.h"
#pragma warning(disable:4100)


wstring OpenSingleFile(HWND hh, const wchar_t* filter, int fidx, const wchar_t* initf, const wchar_t* dir, const wchar_t* title)
{
	OPENFILENAME of = { 0 };
	of.lStructSize = sizeof(of);
	of.hwndOwner = hh;
	of.lpstrFilter = filter;
	of.lpstrInitialDir = dir;
	of.nFilterIndex = fidx;
	vector<wchar_t> fnx(10000);
	of.lpstrFile = fnx.data();
	if (initf)
		wcscpy_s(fnx.data(), 10000, initf);
	of.nMaxFile = 10000;
	of.lpstrTitle = title;
	of.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER;
	if (!GetOpenFileName(&of))
		return wstring(L"");
	return wstring(fnx.data());
}
wstring SaveFile(HWND hh, const wchar_t* filter, int fidx, const wchar_t* initf, const wchar_t* dir, const wchar_t* title, const wchar_t* defext)
{
	OPENFILENAME of = { 0 };
	of.lStructSize = sizeof(of);
	of.hwndOwner = hh;
	of.lpstrFilter = filter;
	of.lpstrInitialDir = dir;
	of.nFilterIndex = fidx;
	vector<wchar_t> fnx(10000);
	of.lpstrFile = fnx.data();
	if (initf)
		wcscpy_s(fnx.data(), 10000, initf);
	of.nMaxFile = 10000;
	of.lpstrTitle = title;
	of.lpstrDefExt = defext;
	of.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_EXPLORER;
	if (!GetSaveFileName(&of))
		return wstring(L"");
	return wstring(fnx.data());
}

/*







int mid = -1;
HMIDIIN hmid = 0;
*/
/*

int __stdcall WinMain(HINSTANCE h, HINSTANCE, LPSTR, int)
	{
	WSADATA wData;
	WSAStartup(MAKEWORD(2, 2), &wData);
	CoInitializeEx(0, COINIT_APARTMENTTHREADED);
	INITCOMMONCONTROLSEX icex = { 0 };
	icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_DATE_CLASSES | ICC_WIN95_CLASSES;
	icex.dwSize = sizeof(icex);
	InitCommonControlsEx(&icex);
	InitCommonControls();

	WNDCLASSEX wClass = { 0 };
	wClass.cbSize = sizeof(wClass);

	wClass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_PARENTDC;
	wClass.lpfnWndProc = (WNDPROC)SYNTH_WP;
	wClass.hInstance = h;
	wClass.hIcon = 0;
	wClass.hCursor = LoadCursor(0, IDC_ARROW);
	wClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wClass.lpszClassName = _T("SYNTHCLASS");
	wClass.hIconSm = 0;
	RegisterClassEx(&wClass);



	TASKDIALOGCONFIG tc = { 0 };
	tc.cbSize = sizeof(tc);
	tc.hwndParent = 0;
	tc.pszWindowTitle = ttitle;
	//PCWSTR ic2 = TD_INFORMATION_ICON;
	tc.hMainIcon = 0;
	tc.dwFlags = TDF_ENABLE_HYPERLINKS | TDF_CAN_BE_MINIMIZED | TDF_USE_COMMAND_LINKS;

	tc.pszMainInstruction = ttitle;
	tc.pszContent = L"Synthesizer with VST.\r\n";
	tc.dwCommonButtons = 0;
	//	tc.pszFooter = foot().c_str();
	TASKDIALOG_BUTTON b2[8] = { 0 };
	b2[0].pszButtonText = L"Open Setup";
	b2[1].pszButtonText = L"Save Setup";
	b2[2].pszButtonText = L"Add VST";
	b2[3].pszButtonText = L"Edit VST";
	b2[4].pszButtonText = L"Remove VST";
	b2[5].pszButtonText = L"Play (Keyboard)";
	b2[6].pszButtonText = L"Play (MIDI)";
	b2[7].pszButtonText = L"Exit";
	b2[0].nButtonID = 101;
	b2[1].nButtonID = 102;
	b2[2].nButtonID = 103;
	b2[3].nButtonID = 104;
	b2[4].nButtonID = 105;
	b2[5].nButtonID = 106;
	b2[6].nButtonID = 107;
	b2[7].nButtonID = IDCANCEL;

	tc.pButtons = b2;
	tc.cButtons = 8;


	tc.pfCallback = [](_In_ HWND hwnd, _In_ UINT msg, _In_ WPARAM wParam, _In_ LPARAM , _In_ LONG_PTR ) ->HRESULT
	{
		if (msg == TDN_HYPERLINK_CLICKED)
		{
			return S_FALSE;
		}
		if (msg == TDN_CREATED)
		{
			std::thread t(Start);
			t.detach();
		}
		if (msg == TDN_DESTROYED)
		{
		}
		if (msg == TDN_BUTTON_CLICKED)
		{
			if (wParam == 107)
			{
				HMENU m = CreatePopupMenu();
				int l =  midiInGetNumDevs();
				for (int i = 0; i < l; i++)
				{
					MIDIINCAPS c;
					midiInGetDevCaps(i, &c, sizeof(c));
					if (i == mid)
						AppendMenu(m, MF_CHECKED | MF_STRING, i + 1, c.szPname);
					else
						AppendMenu(m, MF_STRING, i + 1, c.szPname);
				}
				POINT p = { 0 };
				GetCursorPos(&p);
				int tcmd = TrackPopupMenu(m, TPM_RETURNCMD | TPM_RIGHTALIGN, p.x, p.y, 0, hwnd, 0);
				if (tcmd == 0)
					return S_FALSE;
				tcmd--;
				if (hmid)
					midiInClose(hmid);
				hmid = 0;
				mid = tcmd;
				if (hS)
					ShowWindow(hS, SW_SHOW);
				else
					hS = CreateWindowEx(0, L"SYNTHCLASS", L"Synthesizer", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwnd, 0, 0, 0);
				midiInOpen(&hmid, 0, (DWORD_PTR)hS, 0, CALLBACK_WINDOW);
				if (hmid)
					midiInStart(hmid);

				return S_FALSE;
			}
			if (wParam == 106)
			{
				if (hS)
					ShowWindow(hS, SW_SHOW);
				else
					hS = CreateWindowEx(0, L"SYNTHCLASS", L"Synthesizer", WS_OVERLAPPEDWINDOW | WS_VISIBLE,  CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwnd, 0, 0, 0);
				return S_FALSE;
			}
			if (wParam == 103)
			{
				auto fi = OpenSingleFile(hwnd, L"*.dll,*.vst3\0*.dll;*.vst3\0\0", 0, 0, 0, 0);
				if (fi.empty())
					return S_FALSE;
				return Add(fi);

//				wout = make_shared<WOUT>(WAVE_MAPPER);
			}
			if (wParam == 101)
			{
				auto fi = OpenSingleFile(hwnd, L"*.xml\0*.xml\0\0", 0, 0, 0, 0);
				if (fi.empty())
					return S_FALSE;
				XML3::XML x(fi.c_str());
				for (auto& a : x.GetRootElement())
				{
					vstxlib::ystring fi2 = a.vv("f");
					Add(fi2,a.vv("c"));
				}

				return S_FALSE;
			}
			if (wParam == 102)
			{
				auto fi = SaveFile(hwnd, L"*.xml\0*.xml\0\0", 0, 0, 0, 0, L"xml");
				if (fi.empty())
					return S_FALSE;
	
				DeleteFile(fi.c_str());
				XML3::XML x(fi.c_str());
				v.readlock([&](const vector<VV>& v) {
					for (auto e : v)
					{
						wchar_t mf[1000] = { 0 };
						GetModuleFileName(e.hDLL, mf, 1000);

						auto& el = x.GetRootElement().AddElement("v");
						el.vv("f").SetValue(vstxlib::ystring(mf));
						CComBSTR b;
						e.v->get_State(0, &b);
						if (b)
						{
							auto s = XML3::Char2Base64((const char*)b.operator LPWSTR(), SysStringByteLen(b));
							el.vv("c").SetValue(s.c_str());
//							el.AddContent(s.c_str(),s.length());
						}

					}
					});
				
				x.Save();
				return S_FALSE;
			}


		}
		return S_OK;
	};


	int rv = 0;
	BOOL ch = 0;
	TaskDialogIndirect(&tc, &rv, 0, &ch);

	return rv;


}
*/

#include "..\\pianoroll.hpp"
using namespace PR;
CONV c;
shared_ptr<WOUT> wout;


class M;
volatile long long NW = 0;

struct VV
{
	CComPtr<vstxlib::VSTZ> v;
	HINSTANCE hDLL = 0;
	char* a1 = 0;
	char* a2 = 0;
	INT wi = 0, wo = 0, mi = 0, mo = 0;

	bool operator<(const VV& vx)
	{
		if (mi > 0 && vx.mi == 0)
			return true;
		if (wi == 0 && vx.wi > 0)
			return true;
		return false;
	}
};

tlock<vector<VV>> v;


void Normalize(float* s, int sz, float fA = 0.98f)
{
	float fmax = 0;
	for (int i = 0; i < sz; i++)
	{
		if (fabs(s[i]) > fmax)
			fmax = fabs(s[i]);
	}

	if (fmax >= 0.9)
		return;

	if (fmax > fA)
		return;

	float mulx = 1.0f / fmax;
	mulx *= fA;
	if (mulx > 5)
		return; //duh
	for (int i = 0; i < sz; i++)
		s[i] *= mulx;
}

void Start()
{
	wout = 0;
	wout = make_shared<WOUT>();
	wout->Open(WAVE_MAPPER, SR, 16);
	for (;;)
	{
		// Convert
		int fsz = BS;
		vector<float> d2(fsz);
		int numVSTs = 0;
		v.readlock([&](const vector<VV> & v) {
			for (auto e : v)
			{
				numVSTs++;
				e.v->Process();
				float** fout = (float**)e.a2;
				// Mix to d2
				for (int ff = 0; ff < fsz; ff++)
				{
					d2.data()[ff] += fout[0][ff];
				}
			}
			});

		// Balance
		if (numVSTs)
		{
			for (int ff = 0; ff < fsz; ff++)
				d2.data()[ff] /= (float)numVSTs;
		}

		// Normalize
		Normalize(d2.data(), fsz);


		vector<short> d4(fsz * 2);
		c.f32t16(d2.data(), fsz, d4.data());
		if (wout)
			wout->Write((const char*)d4.data(), fsz * 2);
	}
}

struct W
{
	HWND MainWindow = 0;
	PIANOROLL prx;
	CComPtr<ID2D1HwndRenderTarget> d;
	CComPtr<ID2D1Factory> fa;
	shared_ptr<M> c = make_shared<M>(this);
};

class M : public PIANOROLLCALLBACK
{
public:

	W* w;
	M(W* ww)
	{
		w = ww;
	}

	virtual HRESULT NoteAdded(PIANOROLL*, NOTE* n)
	{
		DWORD e = 0x90;
		e |= n->ch;
		e |= n->vel << 16;
		e |= n->midi << 8;

		vstxlib::MIDI m;
		m.dwEvent = e;
		v.readlock([&](const vector<VV> & v) {
			for (auto& vv : v)
				vv.v->SendMidiDirect((char*)& m, 1,0);
			});
		Sleep(100);
		vstxlib::MIDI m2;
		e &= 0xFFFF;
		m2.dwEvent = e;
		v.readlock([&](const vector<VV> & v) {
			for (auto& vv : v)
				vv.v->SendMidiDirect((char*)& m2, 1,0);
			});
		return S_OK;
	}
	virtual HRESULT NoteRemoved(PIANOROLL*, NOTE*)
	{
		return S_OK;
	}
	virtual void RedrawRequest(PIANOROLL * p, unsigned long long param)
	{
		RECT rc;
		GetClientRect(w->MainWindow, &rc);
		w->d->BeginDraw();
		w->prx.Paint(w->d, rc, param);
		w->d->EndDraw();
	}
	virtual HRESULT OnNoteChange(PIANOROLL* pr, NOTE * n1, NOTE * n2)
	{
		if (n1->midi == n2->midi)
			return S_OK;
		return NoteAdded(pr, n2);
	}
	virtual HRESULT OnNoteSelect(PIANOROLL* pr, NOTE * n, bool s)
	{
		if (s)
			return NoteAdded(pr, n);
		return S_OK;
	}
	virtual void OnPianoOn(PR::PIANOROLL*, int n, int vel, int ch)
	{
		DWORD e = 0x000090;
		e |= ch;
		e |= vel << 16;
		e |= n << 8;

		vstxlib::MIDI m;
		m.dwEvent = e;
		v.readlock([&](const vector<VV> & v) {
			for(auto& vv :v)
				vv.v->SendMidiDirect((char*)& m, 1,0);
			});
	}
	virtual void OnPianoOff(PR::PIANOROLL*, int off, int ch)
	{
		DWORD e = 0x000090;
		e |= ch;
		e |= off << 8;
		vstxlib::MIDI m;
		m.dwEvent = e;
		v.readlock([&](const vector<VV> & v) {
			for (auto& vv : v)
				vv.v->SendMidiDirect((char*)& m, 1,0);
			});
	}


	virtual void RequestVSTSFPresetList(std::unordered_map<int, std::wstring>& n)
	{

	}

};

class MyVSTCallback : public vstxlib::VSTNOTIFICATION
{
	int refNum = 1;
	vstxlib::VSTZ* v;

public:

	void Set(vstxlib::VSTZ* vv)
	{
		v = vv;
	}

	// FUnknown
	DWORD WINAPI AddRef()
	{
		return ++refNum;
	}
	DWORD WINAPI Release()
	{
		if (refNum)
			--refNum;
		if (refNum == 0)
			delete this;
		return refNum;
	}
	HRESULT WINAPI QueryInterface(const IID & riid, void** object)
	{
		if (!object)
			return E_POINTER;

		*object = 0;
		bool F = false;

		if (riid == __uuidof(IUnknown) || riid == __uuidof(vstxlib::VSTNOTIFICATION))
		{
			*object = this;
			F = true;
		}

		if (F)
		{
			AddRef();
			return S_OK;
		}

		return E_NOINTERFACE;
	}

	virtual  HRESULT __stdcall ParameterChanged(
		INT pidx,
		FLOAT)
	{
		return S_OK;
	}

	virtual  HRESULT __stdcall BeforeDelete(
		IUnknown*)
	{
		return S_OK;
	}

	virtual  HRESULT __stdcall ReceiveMidi(
		SAFEARRAY * m)
	{
		return E_NOTIMPL;
	}

	virtual  HRESULT __stdcall SetDirty(INT)
	{
		return S_OK;
	}

	virtual HRESULT __stdcall EditEnded(IUnknown*)
	{
		return S_OK;
	}
};





HACCEL acc = 0;
void New()
{
	auto hh = CreateWindowEx(0,
		_T("CLASS"),
		ttitle,
		WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS |
		WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		0, LoadMenu(GetModuleHandle(0), L"MENU_1"), 0, 0);

	ShowWindow(hh, SW_SHOW);
}

HRESULT Add(wstring fi, string c = "")
{
	CComPtr<vstxlib::VSTZ> vv;
	int rv = 0;
	auto hm = vstxlib::LoadTry(fi.c_str(), rv);
	if (rv == 0)
		return S_FALSE;
	if (rv == 1)
	{
		FreeLibrary(hm);
		return S_FALSE;
	}
	if (rv == 2)
		CreateVST(2, (IUnknown * *)& vv);
	else
		CreateVST(3, (IUnknown * *)& vv);

	if (!vv)
		return S_FALSE;


	auto hr = vv->Load(_bstr_t(fi.c_str()), (ULONG_PTR)hm,0);
	if (FAILED(hr))
		return S_FALSE;

	vv->InstallHandler(new MyVSTCallback());


	if (c.length())
	{
		XML3::BXML out;
		XML3::Base64ToChar(c.c_str(), c.length(), out);
		std::vector<char> oout(out.size());
		memcpy(oout.data(), out.p(), out.size());
		auto b = vstxlib::VectorToSafeArray<char>(oout);
		vv->put_State(0, b);
	}

	VV a;
	vv->get_Ports(&a.wi, &a.wo, &a.mi, &a.mo);
	vv->Reconfigure(SR, BS, BR, 0);
	vv->CreateBuffers3(&a.a1, &a.a2);
	vv->Resume(0);
	vv->Show(0, 1,0);

	a.v = vv;
	a.hDLL = hm;
	v.writelock([&](vector<VV> & vv) 
		{
			vv.push_back(a);
			// sort
			std::sort(vv.begin(), vv.end());
		}
	);
	return S_FALSE;
}


bool Playing = false;
void PlayX(PR::PIANOROLL* prx)
{
	vector<unsigned char> mmi;
	int BpM = 120;
	int TpB = 960;
	auto jmin = (1000 * 60) / BpM;
	unsigned long long AA = 0;
	prx->ToMidi(mmi, TpB, [&](PR::MIDI::MIDIITEM & it) -> HRESULT
		{

			if (!Playing)
				return E_FAIL;
			auto A = it.ti.abs;
			if (A != AA)
			{
				// Sleep before playing
				// in TpB Ticks -> (60/BpM)/1000 ms
				// In X   Ticks -  ?
				Sleep((DWORD)(jmin * (A - AA) / TpB));
			}

			if (it.ff == 0x51)
			{
				// Tempo
				BpM = it.GetTempo();
				jmin = (1000 * 60) / BpM;
			}
			if (it.event > 0)
			{
				auto& notes = prx->GetNotes();
				if (PR::PIANOROLL::IsNoteOn(it.event))
				{
					prx->SelectNote(it.stridx, true);
				}
				if (PR::PIANOROLL::IsNoteOff(it.event))
				{
					prx->SelectNote(it.stridx, false);
				}

				vstxlib::MIDI m;
				m.dwEvent = it.event;
				v.readlock([&](const vector<VV> & v) {
					size_t ly = notes[it.stridx].layer;
					if (ly < v.size())
					{
						v[ly].v->SendMidiDirect((char*)& m, 1,0);
					}
					});
			}
			AA = A;
			return S_OK;
		});

}


LRESULT CALLBACK Main_DP(HWND hh, UINT mm, WPARAM ww, LPARAM ll)
{
	W* w = (W*)GetWindowLongPtr(hh,GWLP_USERDATA);
	switch (mm)
	{
	case WM_CREATE:
	{
		InterlockedIncrement64(&NW);
		w = new W;
		SetWindowLongPtr(hh, GWLP_USERDATA, (LONG_PTR)w);
		w->MainWindow = hh;
		w->prx.SetWindow(hh);
		w->prx.AddCallback(w->c);
		break;
	}

	case WM_COMMAND:
	{
		int LW = LOWORD(ww);
		UNREFERENCED_PARAMETER(LW);

		if (LW == 101)
		{
			New();
		}

		if (LW == 301)
		{
			if (Playing)
				Playing = false;
			else
			{
				Playing = true;
				std::thread pl(PlayX,&w->prx);
				pl.detach();
			}

		}

		if (LW == 201)
		{
			auto fi = OpenSingleFile(hh, L"*.dll,*.vst3\0*.dll;*.vst3\0\0", 0, 0, 0, 0);
			if (fi.empty())
				return 0;
			return Add(fi,"");
		}


		if (LW == 202 || LW == 203)
		{
			HMENU m = CreatePopupMenu();
			int i = 1;
			v.readlock([&](const vector<VV> & v) {
				for (auto e : v)
				{
					CComBSTR b1, b2;
					e.v->GetNameAndVendor(&b1, &b2);
					AppendMenu(m, MF_STRING, i++, b1);

				}
				});

			POINT p = { 0 };
			GetCursorPos(&p);
			int tcmd = TrackPopupMenu(m, TPM_RETURNCMD | TPM_RIGHTALIGN, p.x, p.y, 0, hh, 0);
			if (tcmd == 0)
				return S_FALSE;
			tcmd--;
			v.writelock([&](vector<VV> & v) {

				if (LW == 202)
					v[tcmd].v->Show(0, 1,0);
				if (LW == 203)
				{
					v.erase(v.begin() + tcmd);
				}

				});

			return S_FALSE;
		}

		if (LW == 102)
		{
			auto fi = OpenSingleFile(hh, L"*.xml\0*.xml\0\0", 0, 0, 0, 0);
			if (fi.empty())
				return S_FALSE;
			XML3::XML x(fi.c_str());
			for (auto& a : x.GetRootElement()["vsts"])
			{
				vstxlib::ystring fi2 = a.vv("f");
				Add(fi2, a.vv("c"));
			}

			w->prx.Unser(x.GetRootElement()["roll"]);
			return 0;

		}

		if (LW == 103)
		{
			auto fi = SaveFile(hh, L"*.xml\0*.xml\0\0", 0, 0, 0, 0, L"xml");
			if (fi.empty())
				return S_FALSE;

			DeleteFile(fi.c_str());
			XML3::XML x(fi.c_str());
			
			auto& pel = x.GetRootElement()["roll"];
			w->prx.Ser(pel);

			v.readlock([&](const vector<VV> & v) {
				for (auto e : v)
				{
					wchar_t mf[1000] = { 0 };
					GetModuleFileName(e.hDLL, mf, 1000);

					auto& el = x.GetRootElement()["vsts"].AddElement("v");
					el.vv("f").SetValue(vstxlib::ystring(mf));
					SAFEARRAY* b;
					e.v->get_State(0, &b);
					if (b)
					{
						auto v = vstxlib::SafeArrayToVector<>(b);
						SafeArrayDestroy(b);
						auto s = XML3::Char2Base64((const char*)v.data(), v.size());
						el.vv("c").SetValue(s.c_str());
					}

				}
				});

			x.Save();

		}

		return 0;
	}

	case WM_CLOSE:
	{
		DestroyWindow(hh);
		return 0;
	}

	case WM_DESTROY:
	{
		delete w;
		SetWindowLongPtr(hh, GWLP_USERDATA, (LONG_PTR)0);
		InterlockedDecrement64(&NW);
		if (NW == 0)
			PostQuitMessage(0);
		return 0;
	}


	case WM_KEYDOWN:
	case WM_SYSKEYDOWN:
	{
		w->prx.KeyDown(ww, ll);
		return 0;
	}
	case WM_MOUSEMOVE:
	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_LBUTTONDBLCLK:
	{
		w->prx.Message(mm,ww, ll);
		return 0;
	}
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		BeginPaint(hh, &ps);

		RECT rc;
		GetClientRect(hh, &rc);
		if (!w->fa)
			D2D1CreateFactory(D2D1_FACTORY_TYPE::D2D1_FACTORY_TYPE_MULTI_THREADED, &w->fa);
		if (!w->d)
		{
			//				D2D1_RENDER_TARGET_PROPERTIES p;
			D2D1_HWND_RENDER_TARGET_PROPERTIES hp;
			hp.hwnd = hh;
			hp.pixelSize.width = rc.right;
			hp.pixelSize.height = rc.bottom;
			w->d.Release();

			w->fa->CreateHwndRenderTarget(D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(hh, D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)), &w->d);
		}
		w->d->BeginDraw();
		w->prx.Paint(w->d, rc);
		w->d->EndDraw();
		EndPaint(hh, &ps);
		return 0;
	}

	case WM_SIZE:
	{
		if (!w->d)
			return 0;

		RECT rc;
		GetClientRect(hh, &rc);
		D2D1_SIZE_U u;
		u.width = rc.right;
		u.height = rc.bottom;
		w->d->Resize(u);
		return 0;
	}

	}
	return DefWindowProc(hh, mm, ww, ll);
}


int __stdcall WinMain(HINSTANCE h, HINSTANCE, LPSTR, int)
{
	WSADATA wData;
	WSAStartup(MAKEWORD(2, 2), &wData);
	CoInitializeEx(0, COINIT_APARTMENTTHREADED);
	INITCOMMONCONTROLSEX icex = { 0 };
	icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_DATE_CLASSES | ICC_WIN95_CLASSES;
	icex.dwSize = sizeof(icex);
	InitCommonControlsEx(&icex);
	InitCommonControls();

	WNDCLASSEX wClass = { 0 };
	wClass.cbSize = sizeof(wClass);

	wClass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_PARENTDC;
	wClass.lpfnWndProc = (WNDPROC)Main_DP;
	wClass.hInstance = h;
	wClass.hIcon = 0;
	wClass.hCursor = 0;// LoadCursor(0, IDC_ARROW);
	wClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wClass.lpszClassName = _T("CLASS");
	wClass.hIconSm = 0;
	RegisterClassEx(&wClass);

	acc = LoadAccelerators(h, L"MENU_1");
	New();
	std::thread t(Start);
	t.detach();


	MSG msg;

	while (GetMessage(&msg, 0, 0, 0))
	{
		if (TranslateAccelerator(msg.hwnd, acc, &msg))
			continue;
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return 0;
}