////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////																					////
////																					////
////					 - WmpRenderer.cpp -	    								    ////
////																					////
////																					////
////				Implementation of TextureRenderer Class             		    	////
////						Version  1.0	(August 2001)    			 				////
////																					////
////								  Nadège SONNET     								////
////																					////
////																					////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////

/****************************************************************************
 * Includes
 ****************************************************************************/
/*#include "../Main.h"
#include <initguid.h> 
#include "WmpRenderer.h"*/

#include "../Main.h"
#include <dshow.h>

//$BLG: v4.6a5 - Note: Last compatible version with VS6.0 is DX9.0 from Oct04 but it worsens our current bugs
#include <d3dx8.h>
//#include <d3dx9.h>

#include "WmpRenderer.h"
#include <initguid.h> 

/****************************************************************************
 * Defines
 ****************************************************************************/

//6041fc8c-f864-40fc-a617-77ad189c4906
DEFINE_GUID(MY_RendererFilter, 0x6041fc8c, 0xf864, 0x40fc, 0xa6, 0x17, 0x77, 0xad, 0x18, 0x9c, 0x49, 0x6);


//-----------------------------------------------------------------------------
// Object creation template
//-----------------------------------------------------------------------------
CFactoryTemplate g_Templates[1] = 
{
  { 
  	L"RENDERFILTER", &MY_RendererFilter, CTextureRenderer::CreateInstance
  }
};


int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);

//-----------------------------------------------------------------------------
// Creation of a new instance of this class
//-----------------------------------------------------------------------------
CUnknown * WINAPI CTextureRenderer::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr)
{
MMechostr(0,"rnd: CreateInstance\n");
  CTextureRenderer *punk = new CTextureRenderer(lpunk, phr);
  if (punk == NULL) 
  {
    *phr = E_OUTOFMEMORY;
  }
  //$BLG: v4.6a5 - Modif
	//*phr = S_OK;
	else
	{
		*phr = S_OK;
  }
	
   return punk;
} 

//-----------------------------------------------------------------------------
// CTextureRenderer : constructor
//-----------------------------------------------------------------------------
CTextureRenderer::CTextureRenderer( LPUNKNOWN pUnk, HRESULT *phr)
                                   : CBaseVideoRenderer(MY_RendererFilter, 
                                   "RENDERFILTER", pUnk, phr)
{
MMechostr(0,"rnd: CTextureRenderer\n");
	if (pUnk)
	{
		m_pUnknown = pUnk;
		m_pUnknown->AddRef();
  }
	m_mywmpbuf=NULL;
	m_eState=Uninitialized;
}


//-----------------------------------------------------------------------------
// CTextureRenderer : destructor
//-----------------------------------------------------------------------------
CTextureRenderer::~CTextureRenderer()
{
MMechostr(0,"rnd: ~CTextureRenderer\n");
  // Do nothing
	m_eState = Uninitialized ;
	if (m_pUnknown)
  {
		m_pUnknown->Release();
		m_pUnknown = NULL;
	}
	m_mywmpbuf=NULL;
}


//-----------------------------------------------------------------------------
// NonDelegatingQueryInterface : Query the IBaseFilter Interface
//-----------------------------------------------------------------------------
STDMETHODIMP CTextureRenderer::NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
MMechostr(0,"rnd: NonDelegatingQueryInterface\n");
  if (riid == IID_IBaseFilter) 
  {
    return GetInterface((IBaseFilter *)this, ppv);
  } 
	else 
	{
    return CBaseVideoRenderer::NonDelegatingQueryInterface(riid, ppv);
  }
}


//-----------------------------------------------------------------------------
// Initialisation of class parameters
//-----------------------------------------------------------------------------
void CTextureRenderer::Init(IUnknown* pUnknown, mywmpbuf p)
{
MMechostr(0,"rnd: Init\n");
	m_mywmpbuf=p;
	m_mywmpbuf->buf=NULL;
}


//-----------------------------------------------------------------------------
// RenderFile: Builds a filter graph that renders the specified file.
//-----------------------------------------------------------------------------
HRESULT CTextureRenderer::RenderFile(IGraphBuilder *m_pGB, LPCWSTR lpwstrFile, LPCWSTR lpwstrPlayList)
{
MMechostr(0,"rnd: RenderFile\n");
	PostMessage(HScol,WMP_STATE,m_mywmpbuf->id,(LPARAM)WMP_CONTACTING);  
	HRESULT hr;
	hr = m_pGB->RenderFile(lpwstrFile,lpwstrPlayList);
	return hr;
	//return m_pGB->RenderFile(lpwstrFile,lpwstrPlayList);
}

//-----------------------------------------------------------------------------
// CheckMediaType: This method forces the graph to give us an R8G8B8 video
// type, making our copy to texture memory trivial.
//-----------------------------------------------------------------------------
HRESULT CTextureRenderer::CheckMediaType(const CMediaType *pmt)
{
  HRESULT   hr = E_FAIL;
  VIDEOINFO *pvi;
MMechostr(0,"rnd: CheckMediaType\n");  
  // Reject the connection if this is not a video type
  if( *pmt->FormatType() != FORMAT_VideoInfo ) 
	{
    return E_INVALIDARG;
  }
    
  // Only accept RGB24
  pvi = (VIDEOINFO *)pmt->Format();
  if(IsEqualGUID(*pmt->Type(), MEDIATYPE_Video) && IsEqualGUID(*pmt->Subtype(), MEDIASUBTYPE_RGB24))
  {
    hr = S_OK;
  }    

  return hr;
}


//-----------------------------------------------------------------------------
// SetMediaType: Graph connection has been made. 
//-----------------------------------------------------------------------------
HRESULT CTextureRenderer::SetMediaType(const CMediaType *pmt)
{
MMechostr(0,"rnd: SetMediaType\n");
  // Retrieve the size of this media type
  VIDEOINFO *pviBmp;                      // Bitmap info header
  pviBmp = (VIDEOINFO *)pmt->Format();
	
	//m_pBitmapInfo = &(pviBmp->bmiHeader);
  m_lVidWidth  = pviBmp->bmiHeader.biWidth;
  m_lVidHeight = abs(pviBmp->bmiHeader.biHeight);
MMechostr(0,"$BLG: biSizeImage %d\n", pviBmp->bmiHeader.biSizeImage); //320x240x3 ...
  //$BLG: v4.6a5 - Modif
  m_lVidPitch = (m_lVidWidth * 3 + 3) & ~(3); // We are forcing RGB24
  //m_lVidPitch = (m_lVidWidth * 3 + 4) & ~(4); // We are forcing RGB24, 32bits bitmap alignment
MMechostr(0,"%d\n",m_lVidWidth);
MMechostr(0,"%d\n",m_lVidHeight);
MMechostr(0,"%d\n",m_lVidPitch);
MMechostr(0,"%d\n",((1 * 3 + 3) & ~(3))); // 1x3 + 3 & ~3 = 6 & ~3 =  0110 & 00 = 0100 = 4
MMechostr(0,"%d\n",((2 * 3 + 3) & ~(3))); // 2x3 + 3 & ~3 = 9 & ~3 =  1001 & 00 = 1000 = 8
MMechostr(0,"%d\n",((3 * 3 + 3) & ~(3))); // 3x3 + 3 & ~3 = 12 & ~3 = 1100 & 00 = 1100 = 12

	//Initialisation of video buffer 
	m_mywmpbuf->width=m_lVidWidth;
	m_mywmpbuf->height=m_lVidHeight;
	if (m_mywmpbuf->buf)
	{
		//$BLG: v4.6a5 - Log
		MMechostr(0,"Free\n");
		//$BLG: v4.6a5 - Modif
		//free((void*)m_mywmpbuf->buf);
		free((unsigned char*)m_mywmpbuf->buf);
		m_mywmpbuf->buf=NULL;
	}
	
	int l;
	l=m_lVidWidth*m_lVidHeight;
	if (m_mywmpbuf->buf==NULL)
	{
    //$BLG: v4.6a5 - 15bits to 24bits
		//m_mywmpbuf->buf=(short*)malloc(l*2);
		//$BLG: v4.6a5 - Del (Unused)
		//m_mywmpbuf->size=l*2;
		m_mywmpbuf->buf=(unsigned char*)malloc(l*3);
MMechostr(0,"$BLG: l*3 %d\n", l*3);		// OK similar to biImageSize (should be set to this in fact no ?)
		//$BLG: v4.6a5 - Del (Unused)
		//m_mywmpbuf->size=l*3;
		
		if (m_mywmpbuf->buf==NULL)
		{
			//$BLG: v4.6a5 - Modif
			//(MSKDEBUG,"\nWARNING >>> Could not create buffer for wmp");
			MMechostr(MSKDEBUG,"\nWARNING >>> Could not create buffer for wmp");
			return S_FALSE;
		}
	}

  return S_OK;
}


//-----------------------------------------------------------------------------
// DoRenderSample: A sample has been delivered. Copy it to the texture.
//-----------------------------------------------------------------------------
HRESULT CTextureRenderer::DoRenderSample( IMediaSample * pSample )
{
	int i,j;
  BYTE  *pBmpBuffer=NULL;     // Bitmap buffer
  
  MMechostr(0,"rnd: DoRenderSample\n");

  //$BLG: v4.6a5 - Add
  if (m_mywmpbuf->msgPosted == true)
  {
  	MMechostr(0,"WMP - Rendering in progress\n");
  	return S_FALSE;
  }
  
	if (m_mywmpbuf->buf==NULL)
	{
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not create buffer for wmp");
		return S_FALSE;
	}
	
MMechostr(0,"0\n");
  //$BLG: v4.6a5
	//if ((m_eState == Stopped) || (m_eState == Paused)|| (m_eState == Uninitialized))
	if ((m_eState == BLG_STOPPING) || (m_eState == Stopped) || (m_eState == Paused)|| (m_eState == Uninitialized))
	{
		return S_FALSE;
	}

//$BLG: v4.6a5 - Removed next part
// Purpose was to turn data from BGR15 to RGB15
// In wmpBlit(), we turn RGB15 to BGR24 ...
// All code transferred to wmpBlit() - wmp.cpp
// Final result: RGB15 to RGB24

MMechostr(0,"1\n");	    
  // Get the video bitmap buffer
  pSample->GetPointer(&pBmpBuffer);
  
  //$BLG: v4.6a5 - 15bits to 24bits
	//short *pTxtBuffer = m_mywmpbuf->buf;//Texture buffer
	unsigned char *pTxtBuffer = m_mywmpbuf->buf;//Texture buffer
	pBmpBuffer+=m_lVidPitch*(m_lVidHeight-1);
	
MMechostr(0,"$BLG: 2\n");
	for(i=0;i<m_lVidHeight;i++)
	{
		for(j=0;j<m_lVidWidth;j++)
		{
			//$BLG: v4.6a5 - Modif (15 to 24 bits)
			//pTxtBuffer[j]=((pBmpBuffer[3*j]>>3)&0x1f)+((pBmpBuffer[3*j+1]<<2)&0x3e0)+((pBmpBuffer[3*j+2]<<7)&0x7c00);		
			pTxtBuffer[(3*j)+0] = pBmpBuffer[(3*j)+0];
			pTxtBuffer[(3*j)+1] = pBmpBuffer[(3*j)+1];
			pTxtBuffer[(3*j)+2] = pBmpBuffer[(3*j)+2];
/*			
			MMechostr(0,"%d\n",pBmpBuffer[(3*j)+0]);
			MMechostr(0,"%d\n",pBmpBuffer[(3*j)+1]);
			MMechostr(0,"%d\n",pBmpBuffer[(3*j)+2]);
			MMechostr(0,"---\n");
			MMechostr(0,"%d\n",pTxtBuffer[(3*j)+0]);
			MMechostr(0,"%d\n",pTxtBuffer[(3*j)+1]);
			MMechostr(0,"%d\n",pTxtBuffer[(3*j)+2]);
			MMechostr(0,"---\n");
*/			
		}
		//$BLG: v4.6a5 - 15bits to 24bits
		//pTxtBuffer+=m_lVidWidth;
		//pBmpBuffer-=m_lVidPitch;
		pTxtBuffer+=m_lVidWidth*3;
		pBmpBuffer-=m_lVidPitch;       //*3 + bits alignment already included
	}
	MMechostr(0,"In >>>\n");

	
MMechostr(0,"3\n");
	//We replace the callback call by an event posting on the main scol window to avoid problems due 
	//to the fact that DirectShow use his own thread
	if(m_mywmpbuf->msgPosted==false )
	{
		MMechostr(0,"5\n");
		m_mywmpbuf->msgPosted = true;
		MMechostr(0,"6\n");
		//$BLG: v4.6a5 - The message is sometimes lost
		//But this generally generates an error (memory access violation) when used with C3D3
		//Memory management problem somewhere ...
		PostMessage(HScol,WMP_BLIT,m_mywmpbuf->id,(LPARAM)m_mywmpbuf);
		//SendMessage(HScol,WMP_BLIT,m_mywmpbuf->id,(LPARAM)m_mywmpbuf);
		MMechostr(0,"7\n");
	}
	else 
	{
	  MMechostr(0,"-1\n");
	  return S_FALSE;//-1;
	}
MMechostr(0,"10\n");
MMechostr(0,"%d\n", m_mywmpbuf->msgPosted);
  return S_OK;
}


//-----------------------------------------------------------------------------
// OnReceiveFirstSample: The first sample has been delivered. Copy it to the texture.
//-----------------------------------------------------------------------------
void CTextureRenderer::OnReceiveFirstSample(IMediaSample *pMediaSample)
{
MMechostr(0,"rnd: OnReceiveFirstSample\n");
	DoRenderSample(pMediaSample);
}


//-------------------------------------------------------------------------------
// CTextureRenderer::WaitForState(): Wait for the state to change to the given one.
//-------------------------------------------------------------------------------
void CTextureRenderer::WaitForState(FILTER_STATE State,IMediaControl *m_pMC)
{
  // Make sure we have switched to the required state
  LONG lfs;

//$BLG
MMechostr(0,"rnd: WaitForState\n");
if (State == State_Running) {MMechostr(0,"State_Running\n");} else if (State == State_Paused) {MMechostr(0,"State_Paused\n");} else if (State == State_Stopped) {MMechostr(0,"State_Stopped\n");} else {MMechostr(0,"State_Unknown\n");}

  do
  {
    //$BLG: v4.6a5 - Modif (No change apparently)
    m_pMC->GetState(0, &lfs) ;
    //m_pMC->GetState(INFINITE, &lfs) ;
//$BLG: DBG
if (lfs == State_Running) {MMechostr(0,"State_Running\n");} else if (lfs == State_Paused) {MMechostr(0,"State_Paused\n");} else if (lfs == State_Stopped) {MMechostr(0,"State_Stopped\n");} else {MMechostr(0,"State_Unknown\n");}
  } while (State != lfs);
}


//-------------------------------------------------------------------------------
// CTextureRenderer::Stop(): Stops the filter graph (and waits to really stop)
//-------------------------------------------------------------------------------
STDMETHODIMP CTextureRenderer::Stop(IMediaControl *m_pMC)
{
  HRESULT  hr;
MMechostr(0,"rnd: Stop\n");

	if (m_eState==Stopped)
		return S_OK ;
MMechostr(0,"rnd: Not stopped\n");

/*
  //$BLG: v4.6a5 - Add
  //Blocking all rendering, to ensure that a closed application doesn't let the scol.exe process running
  //1) We ensure that current rendering is finished
  do
  {
  	//Just a loop
  	0;
  }
  while (m_mywmpbuf->msgPosted == true);
  //2) We force the state to "Stopping" rigth now - This is to ensure that DirectShow doesn't start a new rendering now ...
  m_eState = BLG_STOPPING;
MMechostr(0, "After loop\n");  
    
	if (! IsGraphReady() )
  {
		MMechostr(MSKDEBUG,"\nCTextureRenderer video playback graph hasn't been built yet");
    return S_FALSE ;
  }
MMechostr(0,"rnd: GraphReady\n");
enum _FilterState lfs; 
  //$BLg: v4.6a5 - Add
  //The Pause method seems to fail when we are closing the application (it works if there's some delay between stopping and closing).
  //This doesn't hinder the application to "visually" close, but the scol.exe process yes ... continueing tu use resources.
  if (m_pMC) {MMechostr(0,"m_pMC\n");} else {MMechostr(0,"no m_pMC\n");}
  hr = GetState(0, &lfs);
  if (lfs == State_Running) {MMechostr(0,"State_Running\n");} else if (lfs == State_Paused) {MMechostr(0,"State_Paused\n");} else if (lfs == State_Stopped) {MMechostr(0,"State_Stopped\n");} else {MMechostr(0,"State_Unknown\n");}
  //if (hr == S_OK) {MMechostr(0,"S_OK\n");}  else if (hr == VFW_S_STATE_INTERMEDIATE) {MMechostr(0,"VFW_S_STATE_INTERMEDIATE\n");} else if (hr == VFW_S_CANT_CUE) {MMechostr(0,"VFW_S_CANT_CUE\n");} else if (hr == E_FAIL) {MMechostr(0,"E_FAIL\n");} else {MMechostr(0,"S_UNKNOWN\n");}
  if (hr == S_OK) {MMechostr(0,"S_OK\n");} else if (hr == E_FAIL) {MMechostr(0,"E_FAIL\n");} else {MMechostr(0,"S_UNKNOWN\n");}
  MMechostr(0,"pause...\n");
  if (FAILED(hr = m_pMC->Pause()))
  {
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not pause the filter graph");
		DisplayWmpErrorMessage(hr);
    return S_FALSE ;  	
  };
  WaitForState(State_Paused, m_pMC);
  MMechostr(0,"<<<...\n");
*/  

/*  
  if (FAILED(hr=m_pMC->Stop()))
	{
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not stop the filter graph");
		DisplayWmpErrorMessage(hr);
    return S_FALSE ;
  }
*/
  if (FAILED(hr=m_pMC->StopWhenReady()))
	{
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not stop the filter graph");
		DisplayWmpErrorMessage(hr);
    return S_FALSE ;
  }
  
MMechostr(0,"rnd: Stopping ?\n");

  WaitForState(State_Stopped,m_pMC) ;
MMechostr(0,"rnd: Stopped\n");
  
  // Some state changes now
  m_eState = Stopped ;
  
  return S_OK ;  // success
}


//-------------------------------------------------------------------------------
// CTextureRenderer::Pause(): Pauses/Cues the filter graph (and waits to really pause)
//-------------------------------------------------------------------------------
STDMETHODIMP CTextureRenderer::Pause(IMediaControl *m_pMC)
{

  HRESULT  hr;
MMechostr(0,"rnd: Pause\n");    
	if (m_eState==Paused)
		return S_OK ;

  if (! IsGraphReady() )
  {
	  MMechostr(MSKDEBUG,"\nCTextureRenderer video playback graph hasn't been built yet");
    return S_FALSE ;
  }

	//StopStreaming();
  if (FAILED(hr=m_pMC->Pause()))
  {
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not pause the filter graph");
		DisplayWmpErrorMessage(hr);
    return S_FALSE ;
  }

  WaitForState(State_Paused,m_pMC) ;
  
  // Some state changes now
  m_eState = Paused ;
  
  return S_OK ;  // success
}


//-------------------------------------------------------------------------------
// CTextureRenderer::Run(): Plays the filter graph (and waits to really start playing)
//-------------------------------------------------------------------------------
STDMETHODIMP
CTextureRenderer::Run(IMediaControl *m_pMC)
{
 	HRESULT  hr;
MMechostr(0,"rnd: Run\n");
	if (m_eState==Playing)
		return S_OK ;

	//StartStreaming();
  //$BLG: v4.6a5 - The WaitForState sometimes fail ...
  //Sometimes it functions too, but we do not get any rendering ...
  if (FAILED(hr=m_pMC->Run()))
  {
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not run the filter graph");
		DisplayWmpErrorMessage(hr);
    return S_FALSE ;
  }
  WaitForState(State_Running,m_pMC) ;
  
  // Some state changes now
  m_eState = Playing ;

MMechostr(0,"rnd: <<<\n");

  return S_OK ;  // success
}
