////////////////////////////////////////////////////////////////////////////////
// MP3.cpp
//
//  MP3-handling code, using DirectShow

#include "stdhdr.h"

#include "MP3.h"
#include "Window.h"
#include "Debug.h"

// Force the inclusion of the libraries we need
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "amstrmid.lib")

////////////////////////////////////////////////////////////////////////////////
// Sound and music support

IGraphBuilder   *g_pGraphBuilder;
IMediaControl   *g_pMediaControl;
IMediaSeeking   *g_pMediaSeeking;
IMediaEventEx   *g_pMediaEventEx;

BOOL InitMP3()
{
    HRESULT h;

    // Initialise COM, so that we can use CoCreateInstance.
    ::CoInitialize(NULL);
    
    // Create an IGraphBuilder object, through which 
    //  we will create a DirectShow graph.
    h = CoCreateInstance(CLSID_FilterGraph, NULL,
                                      CLSCTX_INPROC, IID_IGraphBuilder,
                                      (void **)&g_pGraphBuilder);
    if (FAILED(h))
    {
        DISPLAYERRORCODE(h, "DirectShow could not be initialised. DirectX 8 may not be installed.");
        return FALSE;
    }

    // Get the IMediaControl Interface
    h = g_pGraphBuilder->QueryInterface(IID_IMediaControl,
                                 (void **)&g_pMediaControl);
    if (FAILED(h))
    {
        DISPLAYERRORCODE(h, "DirectShow could not be initialised. DirectX 8 may not be installed.");
        return FALSE;
    }

    // Get the IMediaSeeking Interface
    h = g_pGraphBuilder->QueryInterface(IID_IMediaSeeking,
                                 (void **)&g_pMediaSeeking);
    if (FAILED(h))
    {
        DISPLAYERRORCODE(h, "DirectShow could not be initialised. DirectX 8 may not be installed.");
        return FALSE;
    }

    // Get the IMediaEventEx Interface
    h = g_pGraphBuilder->QueryInterface(IID_IMediaEventEx,
                                 (void **)&g_pMediaEventEx);
    if (FAILED(h))
    {
        DISPLAYERRORCODE(h, "DirectShow could not be initialised. DirectX 8 may not be installed.");
        return FALSE;
    }
    g_pMediaEventEx->SetNotifyWindow((OAHWND)hWndMain, WM_APP, 0);

    return TRUE;
}

IBaseFilter *LoadMP3(LPCWSTR wszFilename)
{
    IBaseFilter *pSource;
    HRESULT     h;

    // Add the new source filter to the graph.
    h = g_pGraphBuilder->AddSourceFilter(wszFilename, wszFilename, &pSource);
    if (FAILED(h))
        return NULL;

    return pSource;
}

void PlayMP3(IBaseFilter *pSource)
{
    IPin            *pPin = NULL;
    IEnumFilters    *pFilterEnum = NULL;
    IBaseFilter     **ppFilters;
    IBaseFilter     *pFilterTemp = NULL;
    int             iFiltCount;
    int             iPos;

    // Get the first output pin of the new source filter. Audio sources 
    // typically have only one output pin, so for most audio cases finding 
    // any output pin is sufficient.
    pSource->FindPin(L"Output", &pPin);  

    // Stop the graph
    g_pMediaControl->Stop();

    // Break all connections on the filters. You can do this by adding 
    // and removing each filter in the graph

    g_pGraphBuilder->EnumFilters(&pFilterEnum);
    
    // Need to know how many filters. If we add/remove filters during the
    // enumeration we'll invalidate the enumerator
    for (iFiltCount = 0; pFilterEnum->Skip(1) == S_OK; iFiltCount++)
        ;

    // Allocate space, then pull out all of the 
    ppFilters = (IBaseFilter **)_alloca(sizeof(IBaseFilter *) * iFiltCount);

    pFilterEnum->Reset();

    for (iPos = 0; pFilterEnum->Next(1, &ppFilters[iPos], NULL) == S_OK; iPos++)
        ;
    
    pFilterEnum->Release();

    for (iPos = 0; iPos < iFiltCount; iPos++) 
    {
        g_pGraphBuilder->RemoveFilter(ppFilters[iPos]);
        
        // Put the filter back
        g_pGraphBuilder->AddFilter(ppFilters[iPos], NULL);

        ppFilters[iPos]->Release();
    }

    // We have the new output pin. Render it
    g_pGraphBuilder->Render(pPin);

    pPin->Release();

    // Re-seek the graph to the beginning
    LONGLONG llPos = 0;
    g_pMediaSeeking->SetPositions(&llPos, AM_SEEKING_AbsolutePositioning,
                                &llPos, AM_SEEKING_NoPositioning);

    // Start the graph
    g_pMediaControl->Run();
}

void ReplayMP3()
{
    if (g_pMediaControl != NULL)
    {
        // Re-seek the graph to the beginning
        LONGLONG llPos = 0;
        g_pMediaSeeking->SetPositions(&llPos, AM_SEEKING_AbsolutePositioning,
                                    &llPos, AM_SEEKING_NoPositioning);

        // Start the graph
        g_pMediaControl->Run();
    }
}

void StopMP3()
{
    // Stop the graph
    if (g_pMediaControl != NULL)
        g_pMediaControl->Stop();
}

void ExitMP3()
{
    if (g_pMediaEventEx != NULL)
    {
        g_pMediaEventEx->Release();
        g_pMediaEventEx = NULL;
    }
    if (g_pMediaSeeking != NULL)
    {
        g_pMediaSeeking->Release();
        g_pMediaSeeking = NULL;
    }
    if (g_pMediaControl != NULL)
    {
        g_pMediaControl->Release();
        g_pMediaControl = NULL;
    }
    if (g_pGraphBuilder != NULL)
    {
        g_pGraphBuilder->Release();
        g_pGraphBuilder = NULL;
    }
}

void HandleMP3Events()
{
    long    evCode, param1, param2;
    HRESULT h;

    for (;;)
    { 
        h = g_pMediaEventEx->GetEvent(&evCode, &param1, &param2, 0);
        if (FAILED(h))
            return;
        
        g_pMediaEventEx->FreeEventParams(evCode, param1, param2);
        OnMP3Finish(evCode);
    } 
}