// LiftoffWin32DesktopTestApp.cpp : Defines the entry point for the application.
//

#include "framework.h"
#include "LiftoffWin32DesktopTestApp.h"
#include "LiftoffInterstitialAds.h"
#include "UiUtilities.h"
#include "LiftoffMediation.h"
#include "LiftoffPrivacy.h"

#include <commctrl.h>

#include "PlacementLoadState.h"
#include "EncodeAndDecodeUtils.h"
#include "JsonUtilities.h"


#include <iostream>
#include <vector>
#include <codecvt>
#include <string>

#include "SendSuperTokenToPlatform.h"

#pragma comment(lib, "comctl32.lib")

/// <summary>
/// This file contains code that is used to create and manage the UI controls in the Win32 desktop test app.
/// For code examples that show how to use the Liftoff SDK, see LiftoffInterstitialAds.cpp and LiftoffMediation.cpp
/// </summary>


void CreateTabControl(HWND hwndParent);

WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name
WCHAR szOtherWindowClass[MAX_LOADSTRING] = L"LiftoffDesktopNewWindowClass";       // demo how to set different window than main window

LiftoffAds* g_sdkInstance; // Declaration only, no initialization
std::vector<PlacementConfigFromServer> g_placements;

HINSTANCE hInst;                                // current instance
HWND hMainWnd;
// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
ATOM                MyRegisterAnotherWindowClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK    OtherWndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

/// <summary>
/// Attach a console window to this Win32 desktop app, so we can see stdout and stderr output.
/// </summary>
void attachConsole() {
	AllocConsole();
	FILE* fp;
	freopen_s(&fp, "CONOUT$", "w", stdout); // Redirect stdout to the console
	freopen_s(&fp, "CONOUT$", "w", stderr); // Redirect stderr to the console
}

/// <summary>
/// Entry point for a Windows application using Unicode (wide characters). Initializes the application, registers window classes, and runs the main message loop.
/// </summary>
/// <param name="hInstance">Handle to the current instance of the application.</param>
/// <param name="hPrevInstance">Handle to the previous instance of the application (unused).</param>
/// <param name="lpCmdLine">Command-line arguments as a Unicode string.</param>
/// <param name="nCmdShow">Flag that specifies how the window is to be shown.</param>
/// <returns>Returns the exit code of the application, typically the wParam from the last message processed.</returns>
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPWSTR    lpCmdLine,
	_In_ int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

	attachConsole();

	// Initialize global strings
	LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadStringW(hInstance, IDC_LIFTOFFWIN32DESKTOPTESTAPP, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);
	MyRegisterAnotherWindowClass(hInstance);

	// Perform application initialization:
	if (!InitInstance(hInstance, nCmdShow))
	{
		return FALSE;
	}

	HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_LIFTOFFWIN32DESKTOPTESTAPP));

	MSG msg;

	// Main message loop:
	while (GetMessage(&msg, nullptr, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int)msg.wParam;
}

// We need to subclass the Tab control so that we can get WM_COMMAND
// messages from controls to our main window function.
LRESULT CALLBACK TabPageWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
WNDPROC oldTabPageWndProc[NUM_TABS];

//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEXW wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_LIFTOFFWIN32DESKTOPTESTAPP));
	wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_LIFTOFFWIN32DESKTOPTESTAPP);
	wcex.lpszClassName = szWindowClass;
	wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassExW(&wcex);
}

ATOM MyRegisterAnotherWindowClass(HINSTANCE hInstance)
{
	WNDCLASSEXW wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = OtherWndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_LIFTOFFWIN32DESKTOPTESTAPP));
	wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = szOtherWindowClass;
	wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassExW(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
	hInst = hInstance; // Store instance handle in our global variable

	for (int i = 0; i < NUM_TABS; i++) {
		oldTabPageWndProc[i] = nullptr; // Initialize the old window procedures to nullptr
	}

	HWND hWnd = hMainWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

	if (!hWnd)
	{
		return FALSE;
	}

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	// Just testing that I can make WebView2 work
	// InitializeWebView(hTabPages[1]);
	// Get appID from ID_EDIT_APP_ID control
	WCHAR appID[MAX_LOADSTRING];
	GetWindowText(hEditAppId, appID, 100);
	std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
	std::string appIDStr = converter.to_bytes(appID);

	return TRUE;
}

void UnSubclassTabPages() {
	for (int i = 0; i < 7; i++) {
		SetWindowLongPtr(hTabPages[i], GWLP_WNDPROC, (LONG_PTR)oldTabPageWndProc[i]);
	}
}

LRESULT CALLBACK TabPageWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	if (message == WM_COMMAND) {
		// Forward the WM_COMMAND message to the main window's procedure
		SendMessage(GetParent(GetParent(hWnd)), message, wParam, lParam);
		return 0;
	}
	int id = GetDlgCtrlID(hWnd) - ID_TABPAGE_BASE;
	return CallWindowProc(oldTabPageWndProc[id], hWnd, message, wParam, lParam);
}

//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int sel = LB_ERR;
	std::wstring placementName;
	int length = 0;

	switch (message)
	{
		case WM_CREATE:
			InitCommonControls(); // Load Common Controls
			CreateTabControl(hWnd);
			break;

		case WM_NOTIFY: {
			NMHDR* nmhdr = (NMHDR*)lParam;
			if (nmhdr->code == TCN_SELCHANGE) {
				int iPage = TabCtrl_GetCurSel(hTab);
				ShowTabPage(iPage);
			}
			break;
		}

		case WM_SIZE:
			ResizeTabControl(hWnd);
			break;

		case WM_LIFTOFF_INIT_SUCCESS:
			UpdatePlacementsUI(hWnd);
			return 0;

		case WM_ENABLE_OR_DISABLE_PLAY_BUTTON:
			UpdatePlayButtonState();
			break;


		case WM_COMMAND:
			{
				int wmId = LOWORD(wParam);
				int wmEvent = HIWORD(wParam);

				switch (wmId)
				{
					case ID_BUTTON_INITIALIZE:
						if (wmEvent == BN_CLICKED) {
							EnableWindow(hButtonInitialize, FALSE);
							InitializeLiftoffAds(hWnd);
							return 0;
						}
						break;

					case ID_BUTTON_LOAD:
						if (wmEvent == BN_CLICKED) {
							HandleLoadAdButton(hWnd);
							return 0;
						}
						break;

					case ID_BUTTON_PLAY:
						if (wmEvent == BN_CLICKED)
						{
							HandlePlayAdButton(hWnd);
							return 0;
						}
						break;

					case ID_BUTTON_PLAY_IN_NEW_WINDOW:
						if (wmEvent == BN_CLICKED)
						{
							HandlePlayAdInNewWindowButton(hWnd);
							return 0;
						}
						break;

					case ID_HB_BUTTON_GET_SUPERTOKEN:
						if (wmEvent == BN_CLICKED) {
							HandleGetSuperTokenButton(hWnd);
							return 0;
						}
						break;

					case ID_HB_BUTTON_DECODE_TOKEN:
						if (wmEvent == BN_CLICKED) {
							HandleDecodeSuperToken(hWnd);
							return 0;
						}
						break;


					case ID_HB_BUTTON_ENCODE_TOKEN:
						// Handle Encode Token button
						if (wmEvent == BN_CLICKED) {
							HandleEncodeTokenButton(hWnd);
							return 0;
						}
						break;

					case ID_HB_BUTTON_ENCODE_ADM:
						if (wmEvent == BN_CLICKED) {
							HandleEncodeAdmButton(hWnd);
							return 0;
						}
						break;

					case ID_HB_BUTTON_DECODE_ADM:
						if (wmEvent == BN_CLICKED) {
							HandleDecodeAdmButton(hWnd);
							return 0;
						}
						break;

					case ID_HB_BUTTON_GET_WINNING_ADM:
						if (wmEvent == BN_CLICKED) {
							HandleGetWinningAdm(hWnd);
							return 0;
						}
						break;

					case ID_HB_BUTTON_GET_ADM:
						if (wmEvent == BN_CLICKED) {
							HandleGetAdmButton(hWnd);
							return 0;
						}
						break;

					case ID_HB_BUTTON_LOAD_AD_FROM_ADM:
						if (wmEvent == BN_CLICKED) {
							HandleLoadAdFromAdm(hWnd);
							return 0;
						}
						break;

					case ID_HB_COMBOBOX_RTB_ENDPOINTS:
						if (wmEvent == CBN_SELCHANGE) {
							GetSelectedRtbEndpoint();
							return 0;
						}
						break;

					case ID_HB_BUTTON_PLAY_AD_FROM_ADM:
						if (wmEvent == BN_CLICKED)
						{
							HandlePlayAdFromAdm(hWnd);
							return 0;
						}
						break;

					case ID_HB_BUTTON_PLAY_AD_FROM_ADM_IN_NEW_WINDOW:
						if (wmEvent == BN_CLICKED)
						{
							HandlePlayAdFromAdmInNewWindow(hWnd);
							return 0;
						}
						break;

					case ID_HB_BUTTON_ADM_EXAMPLE:
						if (wmEvent == BN_CLICKED) {
							SetAdmExampleText();
							return 0;
						}
						break;

					case ID_HB_BUTTON_REALTIME_TOKEN_EXAMPLE:
						if (wmEvent == BN_CLICKED)
						{
							ClearRealtimeReport();
							return 0;
						}
						break;


					case ID_HB_BUTTON_CLEAR_REPORT:
						if (wmEvent == BN_CLICKED)
						{
							ClearRealtimeReport();
							return 0;
						}
						break;

					case ID_HB_BUTTON_COPY_REPORT:
						if (wmEvent == BN_CLICKED) {
							CopyRealtimeReportToClipboard();
							return 0;
						}
						break;

					case IDM_ABOUT:
						DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
						break;

					case IDM_EXIT:
						UnSubclassTabPages();
						DestroyWindow(hWnd);
						break;

					case ID_CHECKBOX_DISABLE_ASHWID_TRACKING:
					{
						int checkState = IsDlgButtonChecked(hWnd, ID_CHECKBOX_DISABLE_ASHWID_TRACKING);
						switch(checkState) {
							case BST_CHECKED:
								LiftoffAds::SetDisableAshwidTracking(true);
								break;
							case BST_UNCHECKED:
								LiftoffAds::SetDisableAshwidTracking(false);
								break;
							// If the button state isn't set, go back to default (false)
							case BST_INDETERMINATE:
								LiftoffAds::SetDisableAshwidTracking(false);
								break;
						}
					}
					break;

					case ID_BUTTON_CCPA_GET:
						if (wmEvent == BN_CLICKED)
						{
							UpdateCcpaStatusLabel();
							return 0;
						}
						break;

					case ID_BUTTON_CCPA_SET: {
						if (wmEvent == BN_CLICKED)
						{
							SetCcpaStatusFromCombo(hWnd);
							return 0;
						}
						break;

					case ID_BUTTON_COPPA_GET:
						if (wmEvent == BN_CLICKED)
						{
							UpdateCoppaStatusLabel();
							return 0;
						}
						break;

					case ID_BUTTON_COPPA_SET:
						if (wmEvent == BN_CLICKED)
						{
							SetCoppaStatusFromCombo(hWnd);
							return 0;
						}
						break;


					case ID_BUTTON_GDPR_GET:
						if (wmEvent == BN_CLICKED)
						{
							UpdateGdprStatusLabel();
							return 0;
						}
						break;

					case ID_BUTTON_GDPR_SET:
						if (wmEvent == BN_CLICKED)
						{
							SetGdprStatusFromCombo(hWnd);
							return 0;
						}
						break;

					}
				}
			}
			break;

		case WM_PAINT:
			HandlePaint(hWnd);
			break;

		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

LRESULT CALLBACK OtherWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	return DefWindowProc(hWnd, message, wParam, lParam);
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
		case WM_INITDIALOG:
			return (INT_PTR)TRUE;

		case WM_COMMAND:
			if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
			{
				EndDialog(hDlg, LOWORD(wParam));
				return (INT_PTR)TRUE;
			}
			break;
	}
	return (INT_PTR)FALSE;
}

void CreateTabControl(HWND hwndParent) {
	hTab = CreateWindow(WC_TABCONTROL, L"", WS_CHILD | WS_VISIBLE | TCS_TABS,
		10, 10, 460, 300, hwndParent, (HMENU)ID_TABCONTROL, NULL, NULL);

	TCITEM tie;
	tie.mask = TCIF_TEXT;

	wchar_t tabNames[7][20] = { L"Main", L"Options", L"Debug", L"Privacy", L"Header Bidding", L"Database", L"Log" };
	for (int i = 0; i < NUM_TABS; i++) {
		tie.pszText = tabNames[i];
		TabCtrl_InsertItem(hTab, i, &tie);

		hTabPages[i] = CreateWindow(L"STATIC", L"", WS_CHILD, 15, 35, 440, 260, hTab, reinterpret_cast<HMENU>(static_cast<INT_PTR>(ID_TABPAGE_BASE + i)), NULL, NULL);
		oldTabPageWndProc[i] = (WNDPROC)SetWindowLongPtr(hTabPages[i], GWLP_WNDPROC, (LONG_PTR)TabPageWndProc);
	}

	// Create controls for Tab 1, Tab 2, Tab 3 and Tab 7
	CreateControlsForTab1(hTabPages[0]);
	CreateControlsForTab2(hTabPages[1]);
	CreateControlsForDebugTab(hTabPages[2]);
	CreatePrivacyTabControls(hTabPages[3]);
	CreateControlsForHeaderBiddingTab(hTabPages[4]);
	CreateControlsForTab7(hTabPages[6]);

	ShowTabPage(0); // Show first tab by default
}

