//Disables console window
#if !_DEBUG
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
#endif

#include <windows.h>

#include <iostream>
#include <sstream>

#include "Util.h"
#include "AudioApi.h"
#include "resource.h"
#include "AsuroTool.h"

int main()
{
	ApplicationData applicationData{};

	startImgui<ApplicationData>(applicationData, init, draw, "Asuro's Tool", 600, 400);
}

void init(DrawData& drawData, ApplicationData& appData)
{
	// Load text font
	ImGuiIO& io = ImGui::GetIO();
	io.Fonts->AddFontFromFileTTF("Montserrat-Regular.ttf", 18.0f);

	// Load icon font
	static const ImWchar icons_ranges[] = { 0xEA01, 0xF2DF, 0 };
	ImFontConfig icons_config;
	icons_config.MergeMode = true;
	icons_config.PixelSnapH = true;
	io.Fonts->AddFontFromFileTTF("remixicon.ttf", 14.0f, &icons_config, icons_ranges);

	// Set up audio device api
	HRESULT audioResult;
	audioResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
	isError(audioResult, "Failed to initialize COM: ");

	audioResult = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&appData.audioData->deviceEnumerator));
	isError(audioResult, "Failed to set up audio device enumerator: ");

	appData.audioData->audioNotificationListener = new AudioNotificationListener(appData.audioData);
	audioResult = appData.audioData->deviceEnumerator->RegisterEndpointNotificationCallback(appData.audioData->audioNotificationListener);
	isError(audioResult, "Failed to register audio notification listener: ");

	reloadDeviceLists(*appData.audioData);

	// Set window icon
	LPWSTR iconId = MAKEINTRESOURCE(IDI_ICON1);
	HANDLE iconLarge = LoadImageW(GetModuleHandle(NULL), iconId, IMAGE_ICON, 64, 64, 0);
	HANDLE iconSmall = LoadImageW(GetModuleHandle(NULL), iconId, IMAGE_ICON, 32, 32, 0);
	SendMessage(drawData.window_handle, WM_SETICON, ICON_BIG, (LPARAM)iconLarge);
	SendMessage(drawData.window_handle, WM_SETICON, ICON_SMALL, (LPARAM)iconSmall);
	SendMessage(drawData.window_handle, WM_SETICON, ICON_SMALL2, (LPARAM)iconSmall);
}

void draw(DrawData& drawData, ApplicationData& appData)
{
	float customYCursor = 0;
	ImVec2 viewportSize = ImGui::GetMainViewport()->Size;

	// Menu Bar
	customYCursor += menuBar(appData).y;

	// Playback Devices
	ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
	ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
	customYCursor += audioDeviceWindow(appData, appData.audioData->playbackDevices, " \xEE\xB8\x84 Playback").y;

	customYCursor += 5.;

	// Recording devices
	ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
	ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
	customYCursor += audioDeviceWindow(appData, appData.audioData->recordingDevices, " \xEE\xBD\x8F Recording").y;

	// Resize viewport
	if (appData.settings.fitWindowHeight)
	{
		drawData.window_size.y = customYCursor;
	}
}

ImVec2 menuBar(ApplicationData& appData)
{
	ImVec2 size{};

	if (ImGui::BeginMainMenuBar())
	{
		if (ImGui::BeginMenu("Settings"))
		{
			ImGui::Checkbox("Show Disabled Devices", &appData.settings.showDisabledDevices);
			ImGui::Checkbox("Fit Window Height", &appData.settings.fitWindowHeight);
			ImGui::EndMenu();
		}

		if (ImGui::BeginMenu("Debug"))
		{
			if (ImGui::Button("Manual Refresh"))
			{
				reloadDeviceLists(*appData.audioData);
			}
			ImGui::EndMenu();
		}
		size = ImGui::GetWindowSize();
		ImGui::EndMainMenuBar();
	}

	return size;
}

bool customButton(const char* id_start, const char* id_end, const char* title, bool visible)
{
	std::string buttonId(id_start);
	buttonId.append(id_end);

	bool result = false;
	if (visible)
	{
		ImGui::PushID(buttonId.c_str());
		result = ImGui::SmallButton(title);
		ImGui::PopID();
	}
	else
	{
		ImGui::InvisibleButton(buttonId.c_str(), ImGui::CalcTextSize(title));
	}

	return result;
}

ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& deviceList, const char* title)
{
	if (ImGui::Begin(title, 0, ImGuiWindowFlags_NoResize))
	{
		if (ImGui::BeginTable("DeviceTable", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders | ImGuiTableFlags_NoSavedSettings))
		{
			ImGui::TableSetupColumn("Devices", ImGuiTableColumnFlags_WidthStretch, 3.);
			ImGui::TableSetupColumn("Volume", ImGuiTableColumnFlags_WidthStretch, 1.);
			ImGui::TableSetupColumn("Defaults", ImGuiTableColumnFlags_WidthFixed, 55.);
			ImGui::TableHeadersRow();

			for (auto& dev : deviceList)
			{
				std::string deviceIdUtf8 = utf8Encode(dev.id);

				if (dev.state == DEVICE_STATE_ACTIVE)
				{
					ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1., 1., 1., 1.));
				}
				else if (!appData.settings.showDisabledDevices)
				{
					continue;
				}
				else
				{
					ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(.7, .7, .7, 1.));
				}

				// Device Name
				ImGui::TableNextRow();
				ImGui::TableNextColumn();

				ImGui::Text(dev.name.c_str());

				// Volume
				ImGui::TableNextColumn();
				if (dev.state == DEVICE_STATE_ACTIVE)
				{


					float volume = log10f(getMeterValue(dev.meterInterface) * 9. + 1.);

					auto drawList = ImGui::GetWindowDrawList();
					ImVec2 windowPos = ImGui::GetWindowPos();
					ImVec2 cursorPos = ImGui::GetCursorScreenPos();
					ImVec2 space = ImGui::GetContentRegionAvail();
					float lineY = cursorPos.y + ImGui::GetTextLineHeight() / 2.;

					drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + space.x, lineY), IM_COL32(120, 120, 120, 255), 2.);
					drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + space.x * volume, lineY), IM_COL32(200, 200, 255, 255), 3.);
				}

				// Defaults
				ImGui::TableNextColumn();
				if (dev.state == DEVICE_STATE_ACTIVE)
				{
					if (dev.isDefaultConsole)
					{
						drawCircle(5, IM_COL32(50, 50, 222, 255));
					}
					if (customButton("bn_d_", deviceIdUtf8.c_str(), "\xEE\xBE\x82", !dev.isDefaultConsole))
					{
						setDefaultAudioDevice(*appData.audioData, dev.id.c_str(), ERole::eConsole);
					}

					ImGui::SameLine();
					if (dev.isDefaultCommunication)
					{
						drawCircle(5, IM_COL32(222, 50, 50, 255));
					}
					if (customButton("bn_c_", deviceIdUtf8.c_str(), "\xEE\xBF\xA9", !dev.isDefaultCommunication))
					{
						setDefaultAudioDevice(*appData.audioData, dev.id.c_str(), ERole::eCommunications);
					}
				}

				ImGui::PopStyleColor();
			}

			ImGui::EndTable();
		}
	}

	ImVec2 size = ImGui::GetWindowSize();

	ImGui::End();
	return size;
}

void drawCircle(float radius, ImU32 color)
{
	ImGui::Dummy(ImVec2(0, 0));
	ImGui::SameLine();
	auto drawList = ImGui::GetWindowDrawList();
	ImVec2 cursorPos = ImGui::GetCursorPos();
	ImVec2 windowPos = ImGui::GetWindowPos();
	drawList->AddCircleFilled(ImVec2(cursorPos.x + windowPos.x, cursorPos.y + windowPos.y + ImGui::GetTextLineHeight() / 2.), radius, color);
}
