#include <windows.h>

#include "AudioApi.h"
#include "AudioNotificationListener.h"


AudioNotificationListener::AudioNotificationListener(std::shared_ptr<AudioData> audioData) : audioData(audioData)
{

}

HRESULT __stdcall AudioNotificationListener::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState)
{
	auto updateDevice = [pwstrDeviceId, dwNewState](std::vector<AudioDevice>& deviceList)
	{
		for (auto& audioDevice : deviceList)
		{
			if (wcscmp(audioDevice.id.c_str(), pwstrDeviceId) == 0)
			{
				audioDevice.state = dwNewState;
			}
		}
	};

	updateDevice(audioData->playbackDevices);
	updateDevice(audioData->recordingDevices);

	return S_OK;
}

HRESULT __stdcall AudioNotificationListener::OnDeviceAdded(LPCWSTR pwstrDeviceId)
{
	IMMDevice* newDevice;
	HRESULT result = audioData->deviceEnumerator->GetDevice(pwstrDeviceId, &newDevice);
	if (SUCCEEDED(result) && newDevice != nullptr)
	{
		// TODO: add to device list
	}

	return result;
}

HRESULT __stdcall AudioNotificationListener::OnDeviceRemoved(LPCWSTR pwstrDeviceId)
{
	auto deleteDevice = [pwstrDeviceId](std::vector<AudioDevice>& deviceList)
	{
		auto deviceListIterator = deviceList.begin();
		while (deviceListIterator != deviceList.end())
		{
			if (wcscmp(deviceListIterator->id.c_str(), pwstrDeviceId) == 0)
			{
				deviceListIterator = deviceList.erase(deviceListIterator);
			}
		}
	};

	deleteDevice(audioData->playbackDevices);
	deleteDevice(audioData->recordingDevices);

	return S_OK;
}

HRESULT __stdcall AudioNotificationListener::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId)
{
	std::vector<AudioDevice>* deviceList;
	if (flow == EDataFlow::eRender)
	{
		deviceList = &audioData->playbackDevices;
	}
	else
	{
		deviceList = &audioData->recordingDevices;
	}

	for (AudioDevice& audioDevice : *deviceList)
	{
		if (wcscmp(audioDevice.id.c_str(), pwstrDefaultDeviceId) == 0)
		{
			switch (role)
			{
			case ERole::eCommunications:
				audioDevice.isDefaultCommunication = true;
				break;
			case ERole::eConsole:
				audioDevice.isDefaultConsole = true;
				break;
			case ERole::eMultimedia:
				audioDevice.isDefaultMedia = true;
				break;
			}
		}
		else
		{
			switch (role)
			{
			case ERole::eCommunications:
				audioDevice.isDefaultCommunication = false;
				break;
			case ERole::eConsole:
				audioDevice.isDefaultConsole = false;
				break;
			case ERole::eMultimedia:
				audioDevice.isDefaultMedia = false;
				break;
			}
		}
	}

	return S_OK;
}

HRESULT __stdcall AudioNotificationListener::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key)
{
	return S_OK;
}

// Copy pasted stuff from Windows SDK Example
// No clue what it does lol
// https://github.com/pauldotknopf/WindowsSDK7-Samples/blob/master/multimedia/audio/osd/endpointMonitor.cpp
HRESULT __stdcall AudioNotificationListener::QueryInterface(REFIID riid, void** ppvObject)
{
	if ((riid == __uuidof(IUnknown)) ||
		(riid == __uuidof(IMMNotificationClient)))
	{
		*ppvObject = static_cast<IMMNotificationClient*>(this);
	}
	else
	{
		*ppvObject = NULL;
		return E_NOINTERFACE;
	}

	AddRef();
	return S_OK;
}

ULONG __stdcall AudioNotificationListener::AddRef(void)
{
	return InterlockedIncrement(&refCount);
}

ULONG __stdcall AudioNotificationListener::Release(void)
{
	long refCountResult = InterlockedDecrement(&refCount);
	if (refCountResult == 0)
	{
		delete this;
	}
	return refCountResult;
}
