// vstx.h


// DLL Exports
extern "C"
{
	HRESULT __stdcall CreateVST(int VstVersion,IUnknown** z);
	HRESULT __stdcall SetLicense(const char* l);
}

#include "xml/xml3all.h"
namespace vstxlib
{
	using namespace std;

#pragma once


	template <typename T = char>
	CComSafeArray<char> VectorToSafeArray(vector<T>& d)
	{
		CComSafeArray<char> s = SafeArrayCreateVector(VT_I1, 0,(ULONG)(d.size() * sizeof(T)));
		void* dd = 0;
		SafeArrayAccessData(s, &dd);
		if (!dd)
			return 0;
		memcpy(dd, d.data(), d.size() * sizeof(T));
		SafeArrayUnaccessData(s);
		return s;
	}
	
	template <typename T = char>
	vector<T> SafeArrayToVector(SAFEARRAY* s)
	{
		vector<T> tt;
		if (!s)
			return tt;

		void* dd = 0;
		SafeArrayAccessData(s, &dd);
		if (!dd)
			return tt;
		LONG sz = 0;
		SafeArrayGetUBound(s, 1, &sz);
		sz++;
		tt.resize(sz / sizeof(T));
		memcpy(tt.data(), dd, tt.size() * sizeof(T));
		SafeArrayUnaccessData(s);
		return tt;
	}

	// ystring class, wstring <-> string wrapper
	class ystring : public std::wstring
	{
	private:
		mutable std::string asc_str_st;
	public:

		// Constructors
		ystring(HWND hh) : std::wstring()
		{
			AssignFromHWND(hh);
		}
		ystring(HWND hh, int ID) : std::wstring()
		{
			AssignFromHWND(GetDlgItem(hh, ID));
		}
		ystring::ystring() : std::wstring()
		{
		}
		ystring(const char* v, int CP = CP_UTF8)
		{
			EqChar(v, CP);
		}
		ystring(const std::string& v, int CP = CP_UTF8)
		{
			EqChar(v.c_str(), CP);
		}
		ystring(const wchar_t* f)
		{
			if (!f)
				return;
			assign(f);
		}
		/*
					// Constructor and format for sprintf-like
					ystring(const wchar_t* f, ...) : std::wstring()
						{
						va_list args;
						va_start(args, f);

						int len = _vscwprintf(f, args) + 100;
						if (len < 8192)
							len = 8192;
						vector<wchar_t> b(len);
						vswprintf_s(b.data(), len, f, args);
						assign(b.data());
						va_end(args);
						}
		*/
		ystring& Format(const wchar_t* f, ...)
		{
			va_list args;
			va_start(args, f);

			int len = _vscwprintf(f, args) + 100;
			if (len < 8192)
				len = 8192;
			vector<wchar_t> b(len);
			vswprintf_s(b.data(), len, f, args);
			assign(b.data());
			va_end(args);
			return *this;
		}

		// operator =
		void operator=(const char* v)
		{
			EqChar(v);
		}
		void operator=(const wchar_t* v)
		{
			assign(v);
		}
		void operator=(const wstring& v)
		{
			assign(v.c_str());
		}
		void operator=(const ystring& v)
		{
			assign(v.c_str());
		}
		void operator=(const string& v)
		{
			EqChar(v.c_str());
		}
		CLSID ToCLSID()
		{
			CLSID a;
			CLSIDFromString(c_str(), &a);
			return a;
		}
		void operator=(CLSID cid)
		{
			wchar_t ad[100] = { 0 };
			StringFromGUID2(cid, ad, 100);
			assign(ad);
		}

		operator const wchar_t*()
		{
			return c_str();
		}

		// asc_str() and a_str() and operator const char*() 
		const std::string& asc_str(int CP = CP_UTF8) const
		{
			const wchar_t* s = c_str();
			int sz = WideCharToMultiByte(CP, 0, s, -1, 0, 0, 0, 0);
			vector<char> d(sz + 100);
			WideCharToMultiByte(CP, 0, s, -1, d.data(), sz + 100, 0, 0);
			asc_str_st = d.data();
			return asc_str_st;
		}
		operator const char*() const
		{
			return a_str();
		}
		const char* a_str(int CP = CP_UTF8) const
		{
			asc_str(CP);
			return asc_str_st.c_str();
		}

		long long ll() const
		{
			return atoll(a_str());
		}

		// Internal Convertor
		void EqChar(const char* v, int CP = CP_UTF8)
		{
			clear();
			if (!v)
				return;
			int sz = MultiByteToWideChar(CP, 0, v, -1, 0, 0);
			vector<wchar_t> d(sz + 100);
			MultiByteToWideChar(CP, 0, v, -1, d.data(), sz + 100);
			assign(d.data());
		}

		// From HWND
		void AssignFromHWND(HWND hh)
		{
			int zl = GetWindowTextLength(hh);
			std::vector<wchar_t> n(zl + 100);
			GetWindowTextW(hh, n.data(), zl + 100);
			assign(n.data());
		}
	};





	// {FC3D2687-76B2-434A-A3F0-14CAA534CF91}
	inline static const GUID CLSID_VSTXLIBINPROC64 =
	{ 0xfc3d2687, 0x76b2, 0x434a, { 0xa3, 0xf0, 0x14, 0xca, 0xa5, 0x34, 0xcf, 0x91 } };
	// {FC3D2687-76B2-434A-A3F0-14CAB534CF91}
	inline static const GUID CLSID_VSTXLIBINPROC32 =
	{ 0xfc3d2687, 0x76b2, 0x434a, { 0xa3, 0xf0, 0x14, 0xca, 0xb5, 0x34, 0xcf, 0x91 } };
	inline const wchar_t* InProg2ID = L"VSTXLib.InProc";


	// {FC3D2687-76B2-434A-A3F0-14CAA534CF92}
	inline static const GUID CLSID_VSTXLIBOUTPROC64 =
	{ 0xfc3d2687, 0x76b2, 0x434a, { 0xa3, 0xf0, 0x14, 0xca, 0xa5, 0x34, 0xcf, 0x92 } };
	// {FC3D2687-76B2-434A-A3F0-14CAB534CF92}
	inline static const GUID CLSID_VSTXLIBOUTPROC32 =
	{ 0xfc3d2687, 0x76b2, 0x434a, { 0xa3, 0xf0, 0x14, 0xca, 0xb5, 0x34, 0xcf, 0x92 } };
	inline const wchar_t* OutProg2ID = L"VSTXLib.OutProc";


	struct VSTPORT
	{
		int busidx = 0;
		int mt = 0;// 0 audio, 1 midi
		int io = 0;// 0 in 1 out
		ystring n;
	};

	struct MIDI : public MIDIEVENT
	{
		MIDI()
		{
			dwDeltaTime = 0;
			dwStreamID = 0;
			dwEvent = 0;
			dwParms[0] = 0;
		}

		bool operator <(const MIDI& m)
		{
			if (dwDeltaTime < m.dwDeltaTime)
				return true;
			return false;
		}

		unsigned long long UserData = 0;
		// We also want to put VST 3.x events
		bool Vst3Exists = 0;
		char Vst3Event[48] = {0};
	};


	struct VSTINMIDIPORT : public VSTPORT
	{
	};


	struct  VSTINAUDIOPORT : public VSTPORT
	{
	};


	struct  VSTOUTMIDIPORT : public VSTPORT
	{
	};

	struct  VSTOUTAUDIOPORT : public VSTPORT
	{
	};

	class VSTPROGRAM
	{
	public:
		ystring n;
	};

	class VSTUNIT
	{
	public:
		int id = 0;
		int parentUnitId = 0;
		int pid = 0;
		ystring n;
	};

	struct VSTSCAN
	{
		wstring f;
		int Type = 0; // Same types as LoadTry
		int wins = 0;
		int wouts = 0;
		int mins = 0;
		int mouts = 0;
	};

	class VSTPARAMETER
	{
	public:
		ystring n;
		ystring dn;
		double v = 0;
		int id = 0;

		bool operator <(const VSTPARAMETER& p2)
		{
			if (id < p2.id)
				return true;
			return false;
		}

		void Ser(XML3::XMLElement& x)
		{
			x.vv("n").SetValue(n.asc_str().c_str());
			x.vv("d").SetValue(dn.asc_str().c_str());
			x.vv("v").SetFormattedValue<double>("%f", v);
			x.vv("i").SetValueInt(id);
		}

		BSTR GetB()
		{
			XML3::XMLElement x;
			Ser(x);
			ystring sr = x.Serialize();
			return SysAllocString(sr.c_str());
		}

		void Unser(XML3::XMLElement& x)
		{
			n = x.vv("n").GetValue();
			dn = x.vv("d").GetValue();
			id = x.vv("i").GetValueInt();
			v = x.vv("v").GetFormattedValue<double>("%f", 0);
		}

		void FromB(BSTR b)
		{
			ystring a = b;
			XML3::XMLElement x;
			x = a.a_str();
			Unser(x);
		}
	};


	inline HMODULE LoadTry(const wchar_t* f, int& v)
	{
		__try
		{
			v = 0;
			auto hm = LoadLibrary(f);
			if (!hm)
			{
				if (GetLastError() == 193)
				{
					v = 5;
					return 0;
				}
			}

			v = 1;
			auto plg = GetProcAddress(hm, "main");
			if (plg)
				v = 2;
			plg = GetProcAddress(hm, "VSTPluginMain");
			if (plg)
				v = 2;
			plg = GetProcAddress(hm, "GetPluginFactory");
			if (plg)
			{
				if (v == 2)
					v = 4;
				else
					v = 3;
			}
			return hm;
		}
		__except (TRUE)
		{
			v = 0;
			return 0;
		}
	}

	void Create2(HMODULE hTLB, IUnknown** z);
	void Create3(HMODULE hTLB, IUnknown** z);
	HRESULT ScanForVST(const std::initializer_list<wstring>& targets, vector<VSTSCAN>& results);
	CComPtr<ITypeLib> __stdcall LoadTheTLB(HINSTANCE hM, wstring& fx);
	
	inline size_t CreateDoubleArrayInContinuousMemory(size_t ibs, int BS, char* p, char*** ibx)
	{
		int sizeofpointer = 8; // Force 64 bit for interop

		size_t ReqArraysBytes1 = ibs * sizeofpointer;
		size_t ReqT1 = ReqArraysBytes1 * BS;
		if (!p || !ibx)
			return ReqT1;

//		auto p2 = p;
//		memset(p2, 0, ReqT1);
		char** ib = (char**)p;
		p += ibs * sizeofpointer;
		for (size_t i = 0; i < ibs; i++)
		{
			ib[i] = (char*)p;
			p += BS;
		}
		*ibx = ib;
		return ReqT1;
	}

};

