#include "WasapiInterface.h"
#include "FileLogger.h"

#include "AudioSynth.h"

#include <vector>
#include <iostream>

#include <Windows.h>
#include <Audioclient.h>
#include <mmdeviceapi.h>
#include <Functiondiscoverykeys_devpkey.h>

WasapiInterface::WasapiInterface()
{

}

WasapiInterface::~WasapiInterface()
{

}

std::unique_ptr<WasapiInterface> WasapiInterface::Create()
{
    return std::make_unique<WasapiInterface>();
}

void WasapiInterface::OpenDevice(const AudioDevicePtr& device)
{
 
}

void WasapiInterface::SetAccessType(const AudioDevicePtr& device)
{

}

void WasapiInterface::SetSampleFormat(const AudioDevicePtr& device)
{

}

void WasapiInterface::SetSampleRate(const AudioDevicePtr& device)
{

}

void WasapiInterface::SetPeriod(const AudioDevicePtr& device)
{

}

void WasapiInterface::SetBufferSize(const AudioDevicePtr& device)
{

}

void WasapiInterface::SetChannelNumber(const AudioDevicePtr& device)
{

}

// REFERENCE_TIME time units per second and per millisecond
#define REFTIMES_PER_SEC  10000000
#define REFTIMES_PER_MILLISEC  10000

#define EXIT_ON_ERROR(hres)  \
              if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk)  \
              if ((punk) != NULL)  \
                { (punk)->Release(); (punk) = NULL; }

const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);

void WasapiInterface::Play(const AudioDevicePtr& device)
{
    std::cout << "Into wasapi play" << std::endl;
    IMMDeviceEnumerator* pEnumerator = nullptr;
    auto hr = CoCreateInstance(CLSID_MMDeviceEnumerator, 
        nullptr,
        CLSCTX_ALL, 
        IID_IMMDeviceEnumerator, 
        (void**)&pEnumerator);
    if (FAILED(hr))
    {
        std::cout << "Failed to create enumerator" << std::endl;
        return;
    }

    IMMDevice* pDevice = nullptr;
    hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
    if (FAILED(hr))
    {
        return;
    }
    std::cout << "Got default endpoint" << std::endl;

    IPropertyStore* pPropertyStore{ nullptr };
    hr = pDevice->OpenPropertyStore(STGM_READ, &pPropertyStore);

    DWORD propCount{ 0 };
    pPropertyStore->GetCount(&propCount);

    PROPVARIANT deviceName;
    PropVariantInit(&deviceName);
    // hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &deviceName);
    hr = pPropertyStore->GetValue(PKEY_DeviceInterface_FriendlyName, &deviceName);
    //PKEY_Device_FriendlyName
    std::cout << "Device name: " << deviceName.pwszVal << std::endl;

    pPropertyStore->Release();


    IAudioClient* pAudioClient = nullptr;
    hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void**)&pAudioClient);
    std::cout << "Activated audio device" << std::endl;
    if (FAILED(hr))
    {
        return;
    }

    WAVEFORMATEX* pwfx = nullptr;
    hr = pAudioClient->GetMixFormat(&pwfx);
    if (FAILED(hr))
    {
        return;
    }
    std::cout << "formatTag: " << pwfx->wFormatTag << std::endl;
    std::cout << "nChannels: " << pwfx->nChannels << std::endl;
    std::cout << "nSamplesPerSec: " << pwfx->nSamplesPerSec << std::endl;
    std::cout << "nAvgBytesPerSec: " << pwfx->nAvgBytesPerSec << std::endl; // nSamplesPerSec*nBlockAlign
    std::cout << "nBlockAlign: " << pwfx->nBlockAlign << std::endl; //nChannels *wBitsPerSample/8 (bytes)
    std::cout << "wBitsPerSample: " << pwfx->wBitsPerSample << std::endl;
    std::cout << "cbSize: " << pwfx->cbSize << std::endl;

    //REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
    //hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0,
    //        hnsRequestedDuration,
    //        0, pwfx, nullptr);
    //if (FAILED(hr))
    //{
    //    return;
    //}

    //// Tell the audio source which format to use.
    ////    hr = pMySource->SetFormat(pwfx);

    //// Get the actual size of the allocated buffer.
    //UINT32 bufferFrameCount;
    //hr = pAudioClient->GetBufferSize(&bufferFrameCount);

    //IAudioRenderClient* pRenderClient = nullptr;
    //hr = pAudioClient->GetService(IID_IAudioRenderClient, (void**)&pRenderClient);

    //// Grab the entire buffer for the initial fill operation.
    //BYTE* pData;
    //hr = pRenderClient->GetBuffer(bufferFrameCount, &pData);

    //// Load the initial data into the shared buffer.
    //DWORD flags = 0;
    //// hr = pMySource->LoadData(bufferFrameCount, pData, &flags);

    //hr = pRenderClient->ReleaseBuffer(bufferFrameCount, flags);

    //// Calculate the actual duration of the allocated buffer.
    //auto hnsActualDuration = (double)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;

    //hr = pAudioClient->Start();  // Start playing.

    //// Each loop fills about half of the shared buffer.
    //while (flags != AUDCLNT_BUFFERFLAGS_SILENT)
    //{
    //    // Sleep for half the buffer duration.
    //    Sleep((DWORD)(hnsActualDuration / REFTIMES_PER_MILLISEC / 2));

    //    // See how much buffer space is available.
    //    UINT32 numFramesPadding;
    //    hr = pAudioClient->GetCurrentPadding(&numFramesPadding);

    //    auto numFramesAvailable = bufferFrameCount - numFramesPadding;

    //    // Grab all the available space in the shared buffer.
    //    hr = pRenderClient->GetBuffer(numFramesAvailable, &pData);

    //    // Get next 1/2-second of data from the audio source.
    //    // hr = pMySource->LoadData(numFramesAvailable, pData, &flags);

    //    hr = pRenderClient->ReleaseBuffer(numFramesAvailable, flags);
    //}
    //
    //// Wait for last data in buffer to play before stopping.
    //Sleep((DWORD)(hnsActualDuration / REFTIMES_PER_MILLISEC / 2));

    //hr = pAudioClient->Stop();  // Stop playing.

    //CoTaskMemFree(pwfx);
    //SAFE_RELEASE(pEnumerator)
    //SAFE_RELEASE(pDevice)
    //SAFE_RELEASE(pAudioClient)
    //SAFE_RELEASE(pRenderClient)

    return;
}
