/////////////////////////////////////////////////////////////////////////////
//
// projectM-wmp.cpp : Implementation of CProjectMwmp
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "projectM-wmp.h"

/////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::CProjectMwmp
// Constructor

HGLRC hrc = NULL; 
bool starting = true;
int width, height;
projectM *globalPM;

CProjectMwmp::CProjectMwmp() :
m_hwndParent(NULL),
m_clrForeground(0x0000FF),
m_nPreset(0)
{
    m_dwAdviseCookie = 0;
}

/////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::~CProjectMwmp
// Destructor

CProjectMwmp::~CProjectMwmp()
{
}

/////////////////////////////////////////////////////////////////////////////
// CProjectMwmp:::FinalConstruct
// Called when an effect is first loaded. Use this function to do one-time
// intializations that could fail (i.e. creating offscreen buffers) instead
// of doing this in the constructor, which cannot return an error.

HRESULT CProjectMwmp::FinalConstruct()
{
    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// CProjectMwmp:::FinalRelease
// Called when an effect is unloaded. Use this function to free any
// resources allocated in FinalConstruct.

void CProjectMwmp::FinalRelease()
{
    ReleaseCore();
}




//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::Render
// Called when an effect should render itself to the screen.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::Render(TimedLevel *pLevels, HDC hdc, RECT *prc)
{   
	if (starting) {
		static PIXELFORMATDESCRIPTOR pfd = {
			sizeof(PIXELFORMATDESCRIPTOR), 1,							
			PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,				
			PFD_TYPE_RGBA, 24,	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0
		};
		SetPixelFormat(hdc, ChoosePixelFormat(hdc,&pfd), &pfd);
		hrc = wglCreateContext(hdc);
		wglMakeCurrent(hdc, hrc);		
		starting = false;

		width = (int)(prc->right - prc->left);
		height = (int)(prc->bottom - prc->top);
	    
		globalPM = new projectM("C:\\Program Files\\projectM\\config.inp");//24,18,0,128,width,height,"C:\\Documents and Settings\\DEV2\\My Documents\\svn\\presets_projectM\\");
		
	}

	int newwidth = (int)(prc->right - prc->left);
	int newheight = (int)(prc->bottom - prc->top);
	if (width!= newwidth || height != newheight)
	{
		width=newwidth;
		height=newheight;
	 	globalPM->projectM_resetGL(width,height);
	}

	//PCM* pcm = globalPM->pcm;

	//pcm->addPCM8(pLevels->waveform);
	wglMakeCurrent(hdc, hrc);	
	globalPM->projectM_resetGL(width,height);
    globalPM->renderFrame();

    SwapBuffers(hdc);
    
    return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::MediaInfo
// Everytime new media is loaded, this method is called to pass the
// number of channels (mono/stereo), the sample rate of the media, and the
// title of the media
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::MediaInfo(LONG lChannelCount, LONG lSampleRate, BSTR bstrTitle )
{
    return S_OK;
}


//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::GetCapabilities
// Returns the capabilities of this effect. Flags that can be returned are:
//	EFFECT_CANGOFULLSCREEN		-- effect supports full-screen rendering
//	EFFECT_HASPROPERTYPAGE		-- effect supports a property page
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::GetCapabilities(DWORD * pdwCapabilities)
{
    if (NULL == pdwCapabilities)
    {
        return E_POINTER;
    }

    *pdwCapabilities = EFFECT_CANGOFULLSCREEN;
    return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::GetTitle
// Invoked when a host wants to obtain the title of the effect
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::GetTitle(BSTR* bstrTitle)
{
    USES_CONVERSION;

    if (NULL == bstrTitle)
    {
        return E_POINTER;
    }

    CComBSTR bstrTemp;
    bstrTemp.LoadString(IDS_EFFECTNAME); 
        
    if ((!bstrTemp) || (0 == bstrTemp.Length()))
    {
        return E_FAIL;
    }

    *bstrTitle = bstrTemp.Detach();

    return S_OK;
}


STDMETHODIMP CProjectMwmp::RenderFullScreen(TimedLevel *pLevels)
{

  // NULL parent window should not happen 
    if (NULL == m_hwndParent)
    {
        return E_UNEXPECTED;
    }

    // At this point the visualization should draw directly into the parent
    // window. This sample just calls windowless render for simplicity.

    HDC hdc = ::GetDC(m_hwndParent);

    if (NULL == hdc)
    {
        return E_FAIL;
    }

    RECT rParent = { 0 };
    ::GetClientRect(m_hwndParent, &rParent);

    Render(pLevels, hdc, &rParent);
    
    ::ReleaseDC(m_hwndParent, hdc);

	return S_OK;
}

STDMETHODIMP CProjectMwmp::GoFullscreen(BOOL fFullScreen)
{
	return S_OK;
}
											
//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::GetPresetTitle
// Invoked when a host wants to obtain the title of the given preset
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::GetPresetTitle(LONG nPreset, BSTR *bstrPresetTitle)
{
    USES_CONVERSION;

    if (NULL == bstrPresetTitle)
    {
        return E_POINTER;
    }    

    CComBSTR bstrTemp;
        
	bstrTemp = "projectM 1.0";
			            
    if ((!bstrTemp) || (0 == bstrTemp.Length()))
    {
        return E_FAIL;
    }

    *bstrPresetTitle = bstrTemp.Detach();

    return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::GetPresetCount
// Invoked when a host wants to obtain the number of supported presets
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::GetPresetCount(LONG *pnPresetCount)
{
    if (NULL == pnPresetCount)
    {
        return E_POINTER;
    }

    *pnPresetCount = 1;

    return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::SetCurrentPreset
// Invoked when a host wants to change the index of the current preset
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::SetCurrentPreset(LONG nPreset)
{   
    m_nPreset = 0;

    return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::GetCurrentPreset
// Invoked when a host wants to obtain the index of the current preset
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::GetCurrentPreset(LONG *pnPreset)
{
    if (NULL == pnPreset)
    {
        return E_POINTER;
    }

    *pnPreset = 0;

    return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::SetCore
// Set WMP core interface
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::SetCore(IWMPCore * pCore)
{
    HRESULT hr = S_OK;

    // release any existing WMP core interfaces
    ReleaseCore();

    // If we get passed a NULL core, this  means
    // that the plugin is being shutdown.

    if (pCore == NULL)
    {
        return S_OK;
    }

    m_spCore = pCore;

    // connect up the event interface
    CComPtr<IConnectionPointContainer>  spConnectionContainer;

    hr = m_spCore->QueryInterface( &spConnectionContainer );

    if (SUCCEEDED(hr))
    {
        hr = spConnectionContainer->FindConnectionPoint( __uuidof(IWMPEvents), &m_spConnectionPoint );
    }

    if (SUCCEEDED(hr))
    {
        hr = m_spConnectionPoint->Advise( GetUnknown(), &m_dwAdviseCookie );

        if ((FAILED(hr)) || (0 == m_dwAdviseCookie))
        {
            m_spConnectionPoint = NULL;
        }
    }

    return hr;
}

//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::Create
// Invoked when the visualization should be initialized.
//
// If hwndParent != NULL, RenderWindowed() will be called and the visualization
// should draw into the window specified by hwndParent. This will be the
// behavior when the visualization is hosted in a window.
//
// If hwndParent == NULL, Render() will be called and the visualization
// should draw into the DC passed to Render(). This will be the behavior when
// the visualization is hosted windowless (like in a skin for example).
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::Create(HWND hwndParent)
{

    m_hwndParent = hwndParent;
	if(!starting) 
	{
		delete(globalPM);
		starting = true;
	}

//
    return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::Destroy
// Invoked when the visualization should be released.
//
// Any resources allocated for ing should be released.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::Destroy()
{
	if (!starting)
	{
	    m_hwndParent = NULL;
		starting = true;
		delete(globalPM);
		return S_OK;
	}
}


char* ConvertBSTRToLPSTR (BSTR bstrIn)
   {
   	LPSTR pszOut = NULL;
   
   	if (bstrIn != NULL)
   	{
   		int nInputStrLen = SysStringLen (bstrIn);
   
   		// Double NULL Termination
   		int nOutputStrLen = WideCharToMultiByte(CP_ACP, 0, bstrIn, nInputStrLen, NULL, 0, 0, 0) + 2;	
   
   		pszOut = new char [nOutputStrLen];
   
   		if (pszOut)
   		{
   		    memset (pszOut, 0x00, sizeof (char)*nOutputStrLen);
   
 		 WideCharToMultiByte (CP_ACP, 0, bstrIn, nInputStrLen, pszOut, nOutputStrLen, 0, 0);
   		}
   	 }
   
   	return pszOut;
   }
//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::NotifyNewMedia
// Invoked when a new media stream begins playing.
//
// The visualization can inspect this object for properties (like name or artist)
// that might be interesting for visualization.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::NotifyNewMedia(IWMPMedia *pMedia)
{
	if (starting == false && pMedia != NULL)
	{
	CComBSTR name;
	pMedia->get_name(&name);	

	LPTSTR pszConvertedCharStr = ConvertBSTRToLPSTR (name);
    std::string strConverted (pszConvertedCharStr);
  
    delete [] pszConvertedCharStr;

	globalPM->projectM_setTitle(strConverted);
    return S_OK;
	}
}



//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::OnWindowMessage
// Window messages sent to the parent window.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::OnWindowMessage(UINT msg, WPARAM WParam, LPARAM LParam, LRESULT *plResultParam )
{
    // return S_OK only if the plugin has handled the window message
    // return S_FALSE to let the defWindowProc handle the message
    return S_FALSE;
}

//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::Windowed
// Called when an effect should render itself to the screen.
//
// The fRequiredRender flag specifies if an update is required, otherwise the
// update is optional. This allows visualizations that are fairly static (for example,
// album art visualizations) to only render when the parent window requires it,
// instead of n times a second for dynamic visualizations.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::RenderWindowed(TimedLevel *pLevels, BOOL fRequiredRender )
{
    // NULL parent window should not happen 
    if (NULL == m_hwndParent)
    {
        return E_UNEXPECTED;
    }

    // At this point the visualization should draw directly into the parent
    // window. This sample just calls windowless render for simplicity.

    HDC hdc = ::GetDC(m_hwndParent);

    if (NULL == hdc)
    {
        return E_FAIL;
    }

    RECT rParent = { 0 };
    ::GetClientRect(m_hwndParent, &rParent);

    Render(pLevels, hdc, &rParent);
    
    ::ReleaseDC(m_hwndParent, hdc);

    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::ReleaseCore
// Release WMP core interfaces
//////////////////////////////////////////////////////////////////////////////
void CProjectMwmp::ReleaseCore()
{
    if (m_spConnectionPoint)
    {
        if (0 != m_dwAdviseCookie)
        {
            m_spConnectionPoint->Unadvise(m_dwAdviseCookie);
            m_dwAdviseCookie = 0;
        }
        m_spConnectionPoint = NULL;
    }

    if (m_spCore)
    {
        m_spCore = NULL;
    }
}

//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::get_foregroundColor
// Property get to retrieve the foregroundColor prop via the public interface.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::get_foregroundColor(BSTR *pVal)
{
	return ColorToWz( pVal, m_clrForeground);
}


//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::put_foregroundColor
// Property put to set the foregroundColor prop via the public interface.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CProjectMwmp::put_foregroundColor(BSTR newVal)
{
	return WzToColor(newVal, &m_clrForeground);
}


//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::WzToColor
// Helper function used to convert a string into a COLORREF.
//////////////////////////////////////////////////////////////////////////////
HRESULT CProjectMwmp::WzToColor(const WCHAR *pwszColor, COLORREF *pcrColor)
{ 
    if (NULL == pwszColor)
    {
        //NULL color string passed in
        return E_POINTER;
    }

    if (0 == lstrlenW(pwszColor))
    {
        //Empty color string passed in
        return E_INVALIDARG;
    }

    if (NULL == pcrColor)
    {
        //NULL output color DWORD passed in
        return E_POINTER;
    }
    
    if (lstrlenW(pwszColor) != 7)
    {
        //hex color string is not of the correct length
        return E_INVALIDARG;
    }

    DWORD dwRet = 0;
    for (int i = 1; i < 7; i++)
    {
        // shift dwRet by 4
        dwRet <<= 4;
        // and add in the value of this string

		if ((pwszColor[i] >= L'0') && (pwszColor[i] <= L'9'))
		{
            dwRet += pwszColor[i] - '0';
        }
        else if ((pwszColor[i] >= L'A') && (pwszColor[i] <= L'F'))
        {
            dwRet += 10 + (pwszColor[i] - L'A');
        }
        else if ((pwszColor[i] >= L'a') && (pwszColor[i] <= L'f'))
        {
            dwRet += 10 + (pwszColor[i] - L'a');
        }
        else
        {
           //Invalid hex digit in color string
            return E_INVALIDARG;
        }
    }

    *pcrColor = SwapBytes(dwRet);

    return S_OK;
} 


//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::ColorToWz
// Helper function used to convert a COLORREF to a BSTR.
//////////////////////////////////////////////////////////////////////////////
HRESULT CProjectMwmp::ColorToWz( BSTR* pbstrColor, COLORREF crColor)
{
    _ASSERT( NULL != pbstrColor );
    _ASSERT( (crColor & 0x00FFFFFF) == crColor );

    *pbstrColor = NULL;

    WCHAR wsz[8];
    HRESULT hr  = S_OK;

    swprintf_s( wsz, sizeof(wsz)/sizeof(wsz[0]), L"#%06X", SwapBytes(crColor) );
    
    *pbstrColor = ::SysAllocString( wsz );

    if (!pbstrColor)
    {
        hr = E_FAIL;
    }

    return hr;
}


//////////////////////////////////////////////////////////////////////////////
// CProjectMwmp::SwapBytes
// Used to convert between a DWORD and COLORREF.  Simply swaps the lowest 
// and 3rd order bytes.
//////////////////////////////////////////////////////////////////////////////
inline DWORD CProjectMwmp::SwapBytes(DWORD dwRet)
{
    return ((dwRet & 0x0000FF00) | ((dwRet & 0x00FF0000) >> 16) | ((dwRet & 0x000000FF) << 16));
}

