#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <windows.h>

#ifdef QTFORPC_EXPORTS
#define QTFORPC_API __declspec(dllexport)
#else
#define QTFORPC_API __declspec(dllimport)
#endif

/***************************** QT TYPE 定义 *****************************/
typedef void QT_VOID;
typedef int  QT_INT;
typedef double  QT_DOUBLE;

typedef void *  QT_VSTR;
typedef char *  QT_STR;
typedef double* QT_DOUBLE_LIST;
typedef const char *  QT_CSTR;
typedef QT_CSTR    QT_MAP;

typedef QT_VOID(*QT_CB)();
typedef QT_VOID(*QT_AB_CB)(const std::string &value);

enum LOG_PRINT_TYPE {
	LOG_PRINT_TYPE_NONE = 0,
	LOG_PRINT_TYPE_STDOUT, //标准输出stdout
	LOG_PRINT_TYPE_FILE,   //文件输出 %appdata%/qtforpc/appkey/
	LOG_PRINT_TYPE_SCREEN, //debug输出
};

//初始化
typedef QT_VOID(*initQTPCFunc)(QT_CSTR strAppKey, QT_CSTR strProcessName, QT_CSTR strTrackDomain, QT_MAP param);
//反初始化
typedef QT_VOID(*finiQTPCFunc)(QT_CB cb);

/***************************** 注册、登录事件 *****************************/
//更新nick及id
typedef QT_VOID(*onProfileSignInFunc)(QT_CSTR userID, QT_CSTR nick);
//登出
typedef QT_VOID(*onProfileSignOffFunc)();

/***************************** 设置自定义设备id *****************************/
typedef QT_VOID(*setCustomDeviceIdFunc)(QT_CSTR deviceId);

/***************************** app状态 *****************************/
//进入后台
typedef QT_VOID(*enterBackgroundFunc)(QT_VSTR pageObj, QT_MAP customProperties);
typedef QT_VOID(*enterBackgroundWithPageNameFunc)(QT_VSTR pageObj, QT_MAP customProperties, QT_CSTR pageName);
//进入前台
typedef QT_VOID(*enterForegroundFunc)(QT_VSTR pageObj, QT_MAP customProperties);
typedef QT_VOID(*enterForegroundWithPageNameFunc)(QT_VSTR pageObj, QT_MAP customProperties, QT_CSTR pageName);

/***************************** 全局透传设置 *****************************/
// args透传
typedef QT_VOID(*registerGlobalPropertyFunc)(QT_CSTR key, QT_CSTR value);
typedef QT_VOID(*getGlobalPropertyFunc)(QT_CSTR key, QT_STR value, QT_INT size);
typedef QT_VOID(*getGlobalPropertiesFunc)(QT_STR properties, QT_INT size);
typedef QT_VOID(*unregisterGlobalPropertyFunc)(QT_CSTR key);

/***************************** 页面事件 *****************************/
//页面开始展现
typedef QT_VOID(*onPageStartFunc)(QT_VSTR pageObj, QT_CSTR pageName);
//页面开始消失
typedef QT_VOID(*onPageEndFunc)(QT_VSTR pageObj);
//把一些属性对作为页面事件的参数
typedef QT_VOID(*updatePagePropertiesFunc)(QT_VSTR pageObj, QT_MAP pageProperties);
//跳过某个容器，用于多容器嵌套
typedef QT_VOID(*skipPageFunc)(QT_VSTR pageObj);

/*******************************QT 埋点事件***********************************/
typedef QT_VOID(*trackEventFunc)(QT_VSTR pageObj, QT_CSTR id, QT_MAP customProperties);

typedef QT_VOID(*trackEventWithPageNameFunc)(QT_VSTR pageObj, QT_CSTR id, QT_MAP customProperties, QT_CSTR pageName);

typedef QT_VOID(*uploadUserProfileFunc)(QT_VSTR pageObj, QT_MAP customProperties);

/***************************** 调试 *****************************/
//开启实时调试模式
typedef QT_VOID(*turnOnRealTimeDebugFunc)(QT_MAP configs);
//关闭实时调试
typedef QT_VOID(*turnOffRealTimeDebugFunc)();
typedef QT_VOID(*setFastUploadModeFunc)();
typedef QT_VOID(*setQTLogFunc)(QT_CSTR strAppKey, QT_CSTR strProcessName, LOG_PRINT_TYPE type);

/******************ABTest*************************/
typedef QT_VOID(*initABTestFunc)(QT_CSTR serverUrl, QT_MAP options);
typedef QT_VOID(*fetchABTestFromCacheFunc)(QT_CSTR paramName, QT_CSTR valueType, QT_MAP defaultValue, QT_AB_CB handler);
typedef QT_VOID(*fetchABTestFromServerFunc)(QT_CSTR paramName, QT_CSTR valueType, QT_MAP defaultValue, QT_AB_CB handler, QT_INT timeoutSeconds);
typedef QT_VOID(*fetchABTestFromCacheThenServerFunc)(QT_CSTR paramName, QT_CSTR valueType, QT_MAP defaultValue, QT_AB_CB handler, QT_INT timeoutSeconds);


typedef struct QT_INTERFACE {
	HMODULE module;
	initQTPCFunc initQTPC;
	finiQTPCFunc finiQTPC;
	onProfileSignInFunc onProfileSignIn;
	onProfileSignOffFunc onProfileSignOff;
	enterBackgroundFunc enterBackground;
	enterBackgroundWithPageNameFunc enterBackgroundWithPageName;
	enterForegroundFunc enterForeground;
	enterForegroundWithPageNameFunc enterForegroundWithPageName;
	registerGlobalPropertyFunc registerGlobalProperty;
	getGlobalPropertyFunc getGlobalProperty;
	getGlobalPropertiesFunc getGlobalProperties;
	unregisterGlobalPropertyFunc unregisterGlobalProperty;
	onPageStartFunc onPageStart;
	onPageEndFunc onPageEnd;
	updatePagePropertiesFunc updatePageProperties;
	skipPageFunc skipPage;
	trackEventFunc trackEvent;
	trackEventWithPageNameFunc trackEventWithPageName;
	uploadUserProfileFunc uploadUserProfile;
	setCustomDeviceIdFunc setCustomDeviceId;
	turnOnRealTimeDebugFunc turnOnRealTimeDebug;
	turnOffRealTimeDebugFunc turnOffRealTimeDebug;
	setFastUploadModeFunc setFastUploadMode;
	setQTLogFunc setQTLog;
	initABTestFunc initABTest;
	fetchABTestFromCacheFunc fetchABTestFromCache;
	fetchABTestFromServerFunc fetchABTestFromServer;
	fetchABTestFromCacheThenServerFunc fetchABTestFromCacheThenServer;
} *QT_INTERFACE_PTR;

#define SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, functionName)				\
	ptr->functionName = (functionName##Func)(GetProcAddress(hDll, #functionName))

#define REGISTER_INTERFACE(dll, ptr)										\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, initQTPC);						\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, finiQTPC);						\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, onProfileSignIn);				\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, onProfileSignOff);				\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, enterBackground);				\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, enterBackgroundWithPageName);	\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, enterForeground);				\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, enterForegroundWithPageName);	\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, registerGlobalProperty);		\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, getGlobalProperty);			\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, getGlobalProperties);			\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, unregisterGlobalProperty);		\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, onPageStart);					\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, onPageEnd);				    \
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, updatePageProperties);			\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, skipPage);						\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, trackEvent);					\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, trackEventWithPageName);		\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, uploadUserProfile);			\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, setCustomDeviceId);			\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, turnOnRealTimeDebug);			\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, turnOffRealTimeDebug);			\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, setFastUploadMode);			\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, setQTLog);						\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, initABTest);					\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, fetchABTestFromCache);			\
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, fetchABTestFromServer);	    \
	SET_FUNC_POINTER_FOR_INTERFACE(dll, ptr, fetchABTestFromCacheThenServer);

extern "C" {
	QT_INTERFACE_PTR loadQTDLL(LPCWSTR  dllPath) {
		try {
			HMODULE hDll = NULL;
			hDll = LoadLibrary(dllPath);
			if (NULL == hDll) {
				DWORD err = GetLastError();
				fprintf(stderr, "Error loading dll: %lx\n", err);
				return NULL;
			}
			QT_INTERFACE_PTR ptr = new QT_INTERFACE;
			if (!ptr)
			{
				return NULL;
			}
			ptr->module = hDll;
			REGISTER_INTERFACE(hDll, ptr);
			return ptr;
		}
		catch (...) {
			return NULL;
		}
	}

	bool freeQTDLL(QT_INTERFACE_PTR ptr) {
		if (ptr && ptr->module) {
			FreeLibrary(ptr->module);
		}
		if (ptr) {
			delete ptr;
		}
		return true;
	}
}

extern"C" {
	/***************************** 初始化 *****************************/
	//初始化
	QTFORPC_API QT_VOID initQTPC(QT_CSTR strAppKey, QT_CSTR strProcessName, QT_CSTR strTrackDomain, QT_MAP param);
	//反初始化
	QTFORPC_API QT_VOID finiQTPC(QT_CB cb);

	/***************************** 注册、登录事件 *****************************/
	//更新nick及id
	QTFORPC_API QT_VOID onProfileSignIn(QT_CSTR userID, QT_CSTR nick);
	//登出
	QTFORPC_API QT_VOID onProfileSignOff();

	/***************************** 设置自定义设备id *****************************/
	QTFORPC_API QT_VOID setCustomDeviceId(QT_CSTR deviceId);

	/***************************** app状态 *****************************/
	//进入后台
	QTFORPC_API QT_VOID enterBackground(QT_VSTR pageObj, QT_MAP customProperties);
	QTFORPC_API QT_VOID enterBackgroundWithPageName(QT_VSTR pageObj, QT_MAP customProperties, QT_CSTR pageName);
	//进入前台
	QTFORPC_API QT_VOID enterForeground(QT_VSTR pageObj, QT_MAP  customProperties);
	QTFORPC_API QT_VOID enterForegroundWithPageName(QT_VSTR pageObj, QT_MAP customProperties, QT_CSTR pageName);
	
	/***************************** 全局透传设置 *****************************/
	// args透传
	QTFORPC_API QT_VOID registerGlobalProperty(QT_CSTR key, QT_CSTR value);
	QTFORPC_API QT_VOID getGlobalProperty(QT_CSTR key, QT_STR value, QT_INT size);
	QTFORPC_API QT_VOID getGlobalProperties(QT_STR properties, QT_INT size);
	QTFORPC_API QT_VOID unregisterGlobalProperty(QT_CSTR key);

	/***************************** 页面事件 *****************************/
	//页面开始展现
	QTFORPC_API QT_VOID onPageStart(QT_VSTR pageObj, QT_CSTR pageName);
	//页面开始消失
	QTFORPC_API QT_VOID onPageEnd(QT_VSTR pageObj);
	//把一些属性对作为页面事件的参数
	QTFORPC_API QT_VOID updatePageProperties(QT_VSTR pageObj, QT_MAP pageProperties);
	//跳过某个容器，用于多容器嵌套
	QTFORPC_API QT_VOID skipPage(QT_VSTR pageObj);

	/******************************* QT 埋点事件 ************************************/
	QTFORPC_API QT_VOID trackEventWithPageName(QT_VSTR pageObj, QT_CSTR id, QT_MAP customProperties, QT_CSTR pageName);
	QTFORPC_API QT_VOID trackEvent(QT_VSTR pageObj, QT_CSTR id, QT_MAP customProperties);
	QTFORPC_API QT_VOID uploadUserProfile(QT_VSTR pageObj, QT_MAP customProperties);

	/***************************** 调试 *****************************/
	//实时调试模式
	QTFORPC_API QT_VOID turnOnRealTimeDebug(QT_MAP configs);
	//关闭实时调试
	QTFORPC_API QT_VOID turnOffRealTimeDebug();
	//快速上传模式
	QTFORPC_API QT_VOID setFastUploadMode();

	// 日志打印 0:无 1:标准输出stdout 2:文件输出%appdata%/qtforpc/appkey/ 3:debug输出
	QTFORPC_API QT_VOID setQTLog(QT_CSTR strAppKey, QT_CSTR strProcessName, LOG_PRINT_TYPE type);

	/******************************ABTest**********************************/
	/** ABTest 初始化
	*   使用案例: qtInterface->initABTest("https://xxxxxx/abtest_results?appkey=xxxx", abOptionsString.c_str());
	*/
	QTFORPC_API QT_VOID initABTest(QT_CSTR serverUrl, QT_MAP options);
	/** ABTest 请求缓存实验
	*   使用案例: qtInterface->fetchABTestFromCache("y_number", "NUMBER", "1", ABCompletionHandler);
	*/
	QTFORPC_API QT_VOID fetchABTestFromCache(QT_CSTR paramName, QT_CSTR valueType, QT_MAP defaultValue, QT_AB_CB handler);
	/** ABTest 请求服务端实验
	*   使用案例: qtInterface->fetchABTestFromServer("y_json", "JSON", "{'a': 1}", ABCompletionHandler, 3000);
	*/
	QTFORPC_API QT_VOID fetchABTestFromServer(QT_CSTR paramName, QT_CSTR valueType, QT_MAP defaultValue, QT_AB_CB handler, QT_INT timeoutSeconds);
	/** ABTest 先请求本地缓存实验结果，如果没有再请求服务端实验结果
	*   使用案例: fetchABTestFromServerThenCache("y_boolean", "BOOLEAN", "false", ABCompletionHandler, 3000);
	*/
	QTFORPC_API QT_VOID fetchABTestFromCacheThenServer(QT_CSTR paramName, QT_CSTR valueType, QT_MAP defaultValue, QT_AB_CB handler, QT_INT timeoutSeconds);
}