/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
////																					////
////																					////
////							 - Wmp.cpp -	    									////
////																					////
////																					////
////				Implementation of WMP API Scol Functions            		    	////
////						Version  1.0	(August 2001)    			 				////
////																					////
////								  Nadège SONNET     								////
////																					////
////																					////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////

/****************************************************************************
 * Includes
 ****************************************************************************/


#include "../Main.h"
#include "WmpRenderer.h"
#include <streams.h>
#include <dmodshow.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 <Dxerr8.h>
//#include <d3dx9.h>
//#include <dxerr9.h>

#include <wininet.h>
#include "Wmp.h"
#include "../colors.h"


/****************************************************************************
 * Define structures
 ****************************************************************************/


struct WMPVolume
{
  long lvolumedB;//DirectSound volume(-10 000 = silence, 0=volume max log variation
	int  ivolumelin;//volume scol 0=volume min : silence, 100=volume max,linera variation 
};
typedef struct WMPVolume *sWMPVolume;


struct MyWmp
{
  struct MyWmpBuf buf;
	IGraphBuilder    *g_pGB;      //GraphBuilder
	IMediaControl    *g_pMC;      //Media Control
	IBasicAudio      *g_pBA;      //Basic Audio
	IMediaEventEx    *g_pME;			//Media Event Ex
	IMediaSeeking    *g_pMS;		  //Media Seeking
	IBaseFilter			 *g_pBF;		  //Base Filter
	CTextureRenderer *pCTR;       //Texture Renderer Filter 
	int		g_iLocation;			      //Stream Location (0 local file, 1 URL file)
	int		g_iMute;				        //Mute parameter
	struct WMPVolume sVolume;		  //Sound volume
};
typedef struct MyWmp *mywmp;


/****************************************************************************
 * Global variables
 ****************************************************************************/

int typeWMP;
int WMP_ID;
int WMP_BLIT;
int WMP_STATE;
int WM_GRAPHNOTIFY;

/*cbmachine ww;
HWND HScol;*/


/****************************************************************************
 * Functions
 ****************************************************************************/
int wmpDestroybis(mywmp s)
{
	long    evCode, param1, param2;
MMechostr(0,"wmp: wmpDestroybis\n");

	if (s->g_pMC)
  {
		s->pCTR->Stop(s->g_pMC);
		s->g_pMC->Release();
		s->g_pMC = NULL;
  }
	if (s->g_pME)
  {
		s->g_pME->SetNotifyWindow(NULL, WM_GRAPHNOTIFY, NULL);
		
		while (s->g_pME->GetEvent(&evCode, &param1, &param2, 0)==S_OK)
		{ 
			s->g_pME->FreeEventParams(evCode, param1, param2);		
		} 
		s->g_pME->Release();
		s->g_pME = NULL;
  }
	if (s->g_pBF)
  {
		s->g_pBF->Release();
		s->g_pBF = NULL;
  }
	if (s->pCTR)
  {
		s->pCTR->NonDelegatingRelease();
		s->pCTR = NULL;
  }
	if (s->g_pBA)
  {
		s->g_pBA->Release();
		s->g_pBA = NULL;
  }
	if (s->g_pMS)
  {
		s->g_pMS->Release();
		s->g_pMS = NULL;
  }
	if (s->g_pGB)
  {
		s->g_pGB->Release();
		s->g_pGB = NULL;
  }
	if (s->buf.buf)
	{
		free(s->buf.buf);
		s->buf.buf=NULL;
	}
	if (s->buf.login)
	{
		free(s->buf.login);
		s->buf.login=NULL;
	}
	if (s->buf.password)
	{
		free(s->buf.password);
		s->buf.password=NULL;
	}
	free((void*)s);

	CoUninitialize ();

	return 0;
}


int wmpDestroy2(mmachine m, int handsys, int objm)
{
	mywmp s;
MMechostr(0,"wmp: wmpDestroy2\n");	
	s=(mywmp)MMfetch(m,OTOP(objm),0);
	if (s==NULL)  return 0;
	MMstore(m,OTOP(objm),0,0);

	return wmpDestroybis(s);
}


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpCreateEx													
//////////////////////////////////////////////////////////////////////////////////////////////

//fun[Chn S S S] WmpStream
int wmpCreateEx(mmachine m)
{
  char* pszURL=NULL;
	mywmp s;
	int p;
	HRESULT hr = S_OK;
	TCHAR g_szFileName[MAX_PATH] = {0};

	// for scan file name of the extracted stream.
	char g_szFileNameCut[5] = "\0";
MMechostr(0,"wmp: wmpCreateEx\n");
	// Initialize COM
  CoInitialize (NULL);

	// Initialization
	s=(mywmp)malloc(sizeof(struct MyWmp));
  s->buf.id=WMP_ID++;
	s->buf.buf=NULL;
	s->buf.m=m;
	s->buf.login=NULL;
	s->buf.password=NULL;
	//$BLG: v4.6a5 - Del (Unused)
	//s->buf.size=0;
	s->buf.width=s->buf.height=0;
 	s->g_pGB=NULL;
	s->g_pMC=NULL;
	s->g_pBA=NULL;
	s->g_pME=NULL;
	s->g_pMS=NULL;
	s->g_pBF=NULL;
	s->pCTR=NULL;
	s->sVolume.ivolumelin=75;
	s->sVolume.lvolumedB=(long)(5000.0*log10(75.0/100.0));
	s->g_iMute=0;
	s->buf.msgPosted=false;

	// Get Login/Password 	
	if ((p=MMpull(m))!=NIL)
	{
		s->buf.password=(char*)malloc(MMsizestr(m,OTOP(p))+1);
		if (s->buf.password) strcpy(s->buf.password,MMstartstr(m,OTOP(p)));
	}
	if ((p=MMpull(m))!=NIL)
	{
		s->buf.login=(char*)malloc(MMsizestr(m,OTOP(p))+1);
		if (s->buf.login) strcpy(s->buf.login,MMstartstr(m,OTOP(p)));
	}

	//Get url (it can be a local file or an url)
	pszURL=MMstartstr(m,OTOP(MMpull(m)));

   // Determine the pszURL file to load
  WCHAR wFileName[MAX_PATH];
  strcpy( g_szFileName, pszURL);

	// Set stream location, treatment.
	// First, standardize the first four characters of the file name, for comparison.
	if (g_szFileName[0] == 'F')
		g_szFileNameCut[0] = 'f';
	else
		g_szFileNameCut[0] = g_szFileName[0];

	if (g_szFileName[1] == 'I')
		g_szFileNameCut[1] = 'i';
	else
		g_szFileNameCut[1] = g_szFileName[1];

	if (g_szFileName[2] == 'L')
		g_szFileNameCut[2] = 'l';
	else
		g_szFileNameCut[2] = g_szFileName[2];

	if (g_szFileName[3] == 'E')
		g_szFileNameCut[3] = 'e';
	else
		g_szFileNameCut[3] = g_szFileName[3];

	
	// Comparison and Set location.
  if ((g_szFileName[1] == ':') || (strcmp (g_szFileNameCut, "file") == 0))	
		// Local file.
		s->g_iLocation = 0;
	else
		// file from URL.
		s->g_iLocation = 1;


  #ifndef UNICODE
      MultiByteToWideChar(CP_ACP, 0, g_szFileName, -1, wFileName, MAX_PATH);
  #else
      lstrcpy(wFileName, g_szFileName);
  #endif

  // Create the graph builder
  if (S_OK!=(hr=CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
                  IID_IGraphBuilder, (void **)&(s->g_pGB))))
  {
		wmpDestroybis(s);
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not create graph builder instance");
		DisplayWmpErrorMessage(hr);
		MMset(m,0,NIL);
		return 0;
  }
	s->g_pGB->AddRef();
	s->g_pGB->SetDefaultSyncSource();

	// Create the texture renderer instance
	s->pCTR= (CTextureRenderer * )(CTextureRenderer::CreateInstance(NULL, &hr));
	if (hr!=S_OK)
	{
		wmpDestroybis(s);
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not create texture renderer filter instance");
		DisplayWmpErrorMessage(hr);
		MMset(m,0,NIL);
		return 0;
  }
	s->pCTR->NonDelegatingAddRef();

	//Init the texture renderer filter
	s->pCTR->Init(NULL,&(s->buf));

	// Query base filter interface for texture renderer filter
	if (S_OK!=(hr=s->pCTR->NonDelegatingQueryInterface(IID_IBaseFilter, (void **)&(s->g_pBF))))
	{
		wmpDestroybis(s);
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not NonDelegatingQueryInterface of IBaseFilter");
		DisplayWmpErrorMessage(hr);
		MMset(m,0,NIL);
		return 0;
  }
	s->g_pBF->AddRef();

	// Add renderer filter
	if (S_OK!=(hr=s->g_pGB->AddFilter(s->g_pBF, L"RENDERFILTER")))
	{
		wmpDestroybis(s);
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not add Texture renderer filter in graph builder");
		DisplayWmpErrorMessage(hr);
		MMset(m,0,NIL);
		return 0;
	}

	//Create the filter graph
  //$BLG: v4.6a5 - Modif
  //Note: Error C00D0013 or "Cannot handle your request in a timely manner. Please try again later"
  //Note: Error 0x800704c9 is ERROR_CONNECTION_REFUSED, "The remote system refused the network connection."
  //These errors seem to be the ones that regularly trigger this error message.
  //Added a short loop to try to reconnect to the file in case of error.
  //Seems to have solved the problem.
  int iBLG_count = 0;
  int iBLG_max_try = 8;
  hr = -1;
  MMechostr(0,"\n$BLG\n");
  while ((hr != S_OK) && (iBLG_count < iBLG_max_try))
  {
  	hr=s->pCTR->RenderFile(s->g_pGB,wFileName,NULL);
  	if (hr != S_OK) DisplayWmpErrorMessage(hr);
  	iBLG_count++;
  }
	if (hr != S_OK)
	//if (S_OK!=(hr=s->pCTR->RenderFile(s->g_pGB,wFileName,NULL)))
  {
		wmpDestroybis(s);
    MMechostr(MSKDEBUG,"\nWARNING >>> Could not render file of the filter graph");
		DisplayWmpErrorMessage(hr);
		MMset(m,0,NIL);
		return 0;
  }

	// Get the graph's media control, event, seeking & basic audio interfaces
  if (S_OK!=(hr = s->g_pGB->QueryInterface(IID_IMediaControl, (void**)&(s->g_pMC))))
	{
		wmpDestroybis(s);
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not QueryInterface of IMediaControl");
		DisplayWmpErrorMessage(hr);
		MMset(m,0,NIL);
		return 0;
	}
	s->g_pMC->AddRef();

	if (S_OK!=(hr = s->g_pGB->QueryInterface(IID_IBasicAudio, (void**)&(s->g_pBA))))
	{
		wmpDestroybis(s);
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not QueryInterface of IBasicAudio");
		DisplayWmpErrorMessage(hr);
		MMset(m,0,NIL);
		return 0;
	}
	s->g_pBA->AddRef();

	if (S_OK!=(hr = s->g_pGB->QueryInterface(IID_IMediaSeeking, (void**)&(s->g_pMS))))
	{
		wmpDestroybis(s);
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not QueryInterface of IMediaSeeking");
		DisplayWmpErrorMessage(hr);
		MMset(m,0,NIL);
		return 0;
	}
	s->g_pMS->AddRef();

	if (S_OK!=(hr = s->g_pGB->QueryInterface(IID_IMediaEventEx, (void**)&(s->g_pME))))
	{
		wmpDestroybis(s);
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not QueryInterface of IMediaEventEx");
		DisplayWmpErrorMessage(hr);
		MMset(m,0,NIL);
		return 0;
	}
	s->g_pME->AddRef();


	//Notify main scol window for graph event messages (associated with DLGGraphEvent function)
	if (S_OK!=(s->g_pME->SetNotifyWindow((OAHWND)HScol, WM_GRAPHNOTIFY, (LONG)s)))
	{
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not Notify Scol Window for WM_GRAPHNOTIFY message");
		DisplayWmpErrorMessage(hr);
	}


	//Set time format guid for the stream (100 nanoseconds units)
	if (S_OK!=(hr=s->g_pMS->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME)))
  {
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not set 100 nanoseconds units time format for the filter graph");
		DisplayWmpErrorMessage(hr);
  }

	//Post message video or audio file opened
	PostMessage (HScol,WMP_STATE,s->buf.id,(LPARAM)WMP_ONOPENED);

	//Set initial volume
	if (S_OK!=(hr=s->g_pBA->put_Volume(s->sVolume.lvolumedB)))
  {
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not set the volume of the filter graph");
		DisplayWmpErrorMessage(hr);
  }

	//Play the file 
	if (S_OK!=(hr=s->pCTR->Run(s->g_pMC)))
  {
    MMechostr(MSKDEBUG,"\nWARNING >>> Could not run the filter graph");
		DisplayWmpErrorMessage(hr);
  }

  p=MMmalloc(m,16,TYPEBUF); 
  
  if (p==NIL) return MERRMEM;
  MMstore(m,p,0,(int)s);
	if (MMpush(m,PTOO(p)))return MERRMEM;
	MMechostr(MSKDEBUG,"wmp started %d\n",s->buf.id);
	return OBJcreate(m,typeWMP,s->buf.id,-1,0);
}

//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpCreate													
//////////////////////////////////////////////////////////////////////////////////////////////
//fun[Chn S] WmpStream
int wmpCreate(mmachine m)
{
MMechostr(0,"wmp: wmpCreate\n");
	if (MMpush(m,NIL)) return MERRMEM;
	if (MMpush(m,NIL)) return MERRMEM;
	return wmpCreateEx(m);
}


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpCreateFromFile													
//////////////////////////////////////////////////////////////////////////////////////////////
//fun[Chn P] WmpStream
int wmpCreateFromFile(mmachine m)
{
	int k;
MMechostr(0,"wmp: wmpCreateFromFile\n");	
	if (k=Mpushstrbloc(m,"file:///")) return k;
	if (MMpush(m,MMget(m,1))) return MERRMEM;
	if (k=MBstrcat(m)) return k;
	MMset(m,1,MMget(m,0));
	MMpull(m);
	if (MMpush(m,NIL)) return MERRMEM;
	if (MMpush(m,NIL)) return MERRMEM;
	return wmpCreateEx(m);
}


//$LB (07/08/2004)
// fun [WmpStream] [I I]
int wmpGetSize (mmachine m)
{
  int p;
  mywmp r;
MMechostr(0,"wmp: wmpGetSize\n");
  p = MMpull(m);
  if (p==NIL) return MMpush(m, NIL);
  p>>=1;
  r = (mywmp) MMfetch(m,p,0);
  if ((r==NULL)||(r->g_pMC==NULL)||(r->pCTR==NULL)) return MMpush(m,NIL);

  MMpush(m, (r->buf.width)<<1);
  MMpush(m, (r->buf.height)<<1);
  MMpush(m, 2<<1);
  MBdeftab (m);
  return 0;
}  


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpDestroy													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun[WmpStream] I
int wmpDestroy(mmachine m)
{
	int p;
MMechostr(0,"wmp: wmpDestroy\n");
  p=MMget(m,0);
  if (p==NIL) return 0;
	OBJdelTM(m,typeWMP,p);
	MMset(m,0,0);
	return 0;
}

int fillWmpRenderBuffer(mmachine m,renderbuffer rb, int s2)
{
	PtrObjVoid OB ;
  PtrObjBitmap B ;
MMechostr(0,"wmp: fillWmpRenderBuffer\n");
  if ( s2 == NIL ) return -1;
  OB = ( PtrObjVoid ) MMstart(m,OTOP(s2));
  B = ( PtrObjBitmap ) MMstart(m,OTOP(OB->Buffer));
	if (B->bits==NULL) return -1;
	//$LB
  rb->start=(OBJBITMAP_BUFFER)B->bits;
  rb->nextline=B->BPL;
  rb->nbcolumn=B->TailleW;
  rb->nblines=B->TailleH;
  return 0;
}


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpBlit													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun[ObjBitmap WmpStream] ObjBitmap
int wmpBlit(mmachine m)
{
	struct RenderBuffer rb;
	//$BLG - v4.6a5 - Modifying the way the stretching is handled (Removing former variables)
	//int i,j,kx,x,ky,y,p,i0;
	int p;
	//$BLG: v4.6a5 - Add
	//short blg_i1;
	//int blg_i2;
	//$BLG - v4.6a5 - Modifying the way the stretching is handled (New variables)
	int	i=0, j=0, in=0, jn=0, x=0, y=0, ybase=0, yl=0, blg_ySrc=0, blg_yDst=0;
	
	//$BLG: v4.6a5 - Del (15bits code)
	//unsigned char colR, colG, colB;
	mywmp r; 
	//$LB
	OBJBITMAP_BUFFER s;

MMechostr(0,"wmp: wmpBlit\n");
	p=MMpull(m);
	if (p==NIL) return 0;
	r=(mywmp)MMfetch(m,OTOP(p),0);
	if (r==NULL) return 0;
	p=MMget(m,0);
	if (p==NIL) return 0;
	if (fillWmpRenderBuffer(m,&rb,p)) return 0;

	s=rb.start;

	////$BLG - v4.6a5 - Modifying the way the stretching is handled (Del)
	//kx=(r->buf.width<<8)/rb.nbcolumn;
	//ky=(r->buf.height<<8)/rb.nblines;
/*
MMechostr(0,"$BLG\n");
MMechostr(0,"%d\n",kx);
MMechostr(0,"%d\n",ky);
MMechostr(0,"%d\n",rb.nextline);
MMechostr(0,"%d\n",r->buf.width);
MMechostr(0,"%d\n",r->buf.height);
MMechostr(0,"%d\n",rb.nblines);
MMechostr(0,"%d\n",rb.nbcolumn);
*/

  MMechostr(0,"<<< out\n");
//$BLG - v4.6a5 - Modifying the way the stretching is handled (New code)
/*
	y=0;
	for(i=0;i<rb.nblines;i++)
	{
		i0=(y>>8)*r->buf.width;
		x=0;
		for(j=0;j<rb.nbcolumn;j++)
		{
			//$LB
			//$BLG: v4.6a5 - Modifs
			//_COLOR_I16_TO_BGR (r->buf.buf[i0+(x>>8)], &colR, &colG, &colB);
			//s[j*3 + 0]= colB; s[j*3 + 1]= colG; s[j*3 + 2]= colR; 

			//blg_i1 = r->buf.buf[i0+(x>>8)];
			blg_i2 = j*3;
			s[blg_i2+0] = r->buf.buf[i0+(x>>8)+2];  // Blue
			s[blg_i2+1] = r->buf.buf[i0+(x>>8)+1];  // Green
			s[blg_i2+2] = r->buf.buf[i0+(x>>8)+0];  // Red
		
			MMechostr(0,"%d\n",r->buf.buf[i0+(x>>8)+0]);
			MMechostr(0,"%d\n",r->buf.buf[i0+(x>>8)+1]);
			MMechostr(0,"%d\n",r->buf.buf[i0+(x>>8)+2]);
			MMechostr(0,"---\n");
			MMechostr(0,"%d\n",s[blg_i2+0]);
			MMechostr(0,"%d\n",s[blg_i2+1]);
			MMechostr(0,"%d\n",s[blg_i2+2]);
			MMechostr(0,"---\n");

			//$BLG: v4.6a5 - Modif
			//x+=kx;
			x+=kx;
		}
		y+=ky;
		//$LB
		s+=rb.nextline;
	}
*/
	//$BLG: v4.6a5 - own optimized stretching from ZooGL DLL
	for(y=0; y<rb.nblines; y++)
	{
		ybase = y * rb.nbcolumn;
		yl	  = y * r->buf.height / rb.nblines;
		yl	 *= r->buf.width;

		for(x=0; x<rb.nbcolumn; x++)
		{
			blg_ySrc = (yl + x * r->buf.width /rb.nbcolumn) * 3;
			blg_yDst = (ybase + x) * 3;
			
			s[blg_yDst+0] = r->buf.buf[blg_ySrc+0];
			s[blg_yDst+1] = r->buf.buf[blg_ySrc+1];
			s[blg_yDst+2] = r->buf.buf[blg_ySrc+2];
		}
	}

  //$BLG: v4.6a5 - Moved from DLGBlitSample() - (Line 1000)
  //Removed own modif
  //r->buf.msgPosted = false;
  
	return 0;
}


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpPause													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun[WmpStream] I
int wmpPause(mmachine m)
{	
	int p;
	mywmp r;
	HRESULT hr;
MMechostr(0,"wmp: wmpPause\n");
	p=MMpull(m);
	if (p==NIL) return MMpush(m,NIL);
	r=(mywmp)MMfetch(m,OTOP(p),0);
	
	if ((r==NULL)||(r->g_pMC==NULL)||(r->pCTR==NULL)) return MMpush(m,NIL);
	if (S_OK!=(hr=r->pCTR->Pause(r->g_pMC)))
	{
    return MMpush(m,NIL);
  }

	return MMpush(m,0);
}

//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpPlay													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun[WmpStream] I
int wmpPlay(mmachine m)
{
	int p;
	mywmp r;
	HRESULT hr;
MMechostr(0,"wmp: wmpPlay\n");
	p=MMpull(m);
	if (p==NIL) return MMpush(m,NIL);
	r=(mywmp)MMfetch(m,OTOP(p),0);
	
	if ((r==NULL)||(r->g_pMC==NULL)||(r->pCTR==NULL)) return MMpush(m,NIL);

	if (S_OK!=(hr=r->pCTR->Run(r->g_pMC)))
  {
    return MMpush(m,NIL);
  }

	return MMpush(m,0);
}


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpStop													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun[WmpStream] I
int wmpStop(mmachine m)
{
	int p;
	mywmp r;
  LONGLONG pos = 0;
	HRESULT hr;
MMechostr(0,"wmp: wmpStop\n");   
	p=MMpull(m);
	if (p==NIL) return MMpush(m,NIL);
	r=(mywmp)MMfetch(m,OTOP(p),0);
	
	if ((r==NULL)||(r->g_pMC==NULL)||(r->pCTR==NULL)||(r->g_pMS==NULL)) return MMpush(m,NIL);

	if (S_OK!=(hr=r->pCTR->Stop(r->g_pMC)))
  {
    return MMpush(m,NIL);
  }

	if (S_OK!=(hr=r->g_pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning , NULL, AM_SEEKING_NoPositioning)))
  {
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not seek the filter graph");
		DisplayWmpErrorMessage(hr);
    return MMpush(m,NIL);
  }
  
	PostMessage (HScol,WMP_STATE,r->buf.id,(LPARAM)WMP_ONSTOP);

	return MMpush(m,0);
}


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpSeek													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun[WmpStream I] I
int wmpSeek(mmachine m)
{
	int p,i;
	mywmp r;
	LONGLONG llpos,llstoppos,llcurpos;
	HRESULT hr;
MMechostr(0,"wmp: wmpSeek\n");
	i=OTOI(MMpull(m));
	p=MMpull(m);
	if (p==NIL) return MMpush(m,NIL);
	r=(mywmp)MMfetch(m,OTOP(p),0);
	
	if ((r==NULL)||(r->g_pMS==NULL)) return MMpush(m,NIL);

	if (FAILED(hr=r->g_pMS->GetPositions(&llcurpos,&llstoppos)))
  {
   	MMechostr(MSKDEBUG,"\nWARNING >>> Could not get current position and stop position of the stream");
		DisplayWmpErrorMessage(hr);
    return MMpush(m,NIL);
  }

	//Conversion of new position in ms in the current time format (100 nanoseconds units)
	llpos = (LONGLONG)i * ONE_MILLISECOND;
	
	//Limits condition (we are outise the valid position values)
	//not seek if llpos>end of file or <start of file
	if (llpos<0) return MMpush(m,NIL);
	if (llpos> llstoppos) 
		return MMpush(m,NIL);

	if (FAILED(hr=r->g_pMS->SetPositions(&llpos, AM_SEEKING_AbsolutePositioning,// & AM_SEEKING_Segment ,
            &llstoppos, AM_SEEKING_NoPositioning)))
  {
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not seek the filter graph");
		DisplayWmpErrorMessage(hr);
    return MMpush(m,NIL);
  }

	return MMpush(m,0);
}


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpIsSeekable													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun[WmpStream] I
int wmpIsSeekable(mmachine m)
{
	int p;
	mywmp r;
	HRESULT hr;
MMechostr(0,"wmp: wmpIsSeekable\n");
	p=MMpull(m);
	if (p==NIL) return MMpush(m,NIL);
	r=(mywmp)MMfetch(m,OTOP(p),0);

	if ((r==NULL)||(r->g_pMS==NULL)) return MMpush(m,NIL);
	
	if (r->g_iLocation == 1) // URL file.
	{
		DWORD dwCaps;

		dwCaps = AM_SEEKING_CanSeekAbsolute|AM_SEEKING_CanSeekForwards|AM_SEEKING_CanSeekBackwards|
				 AM_SEEKING_CanGetCurrentPos|AM_SEEKING_CanGetStopPos|AM_SEEKING_CanGetStopPos|
				 AM_SEEKING_CanGetDuration|AM_SEEKING_CanPlayBackwards|AM_SEEKING_CanDoSegments;

		if (FAILED(hr = r->g_pMS->CheckCapabilities(&dwCaps)))
		{
			MMechostr(MSKDEBUG,"\nWARNING >>> Could not check seeking capabilities of the stream");
			DisplayWmpErrorMessage(hr);
			return MMpush(m,ITOO(0));
		} 
		else if (hr == S_OK || hr == S_FALSE) // all capabilities are availables.
		{
			return MMpush(m,ITOO(1));
		}
		else if (hr == S_FALSE)  		// The stream might have some of the capabilities.
		{
			// The most importants are "CanGetStopPos" and "CanGetDuration" capabilities for 
			// seeking implemented here.
			r->g_pMS->GetCapabilities(&dwCaps);
			if ((AM_SEEKING_CanGetStopPos & dwCaps) && (AM_SEEKING_CanGetDuration & dwCaps))
				return MMpush(m,ITOO(1));
			else
				return MMpush(m,ITOO(0));
		}

		return MMpush(m,ITOO(0));
	}
	else
		// A local file is always seekable
		return MMpush(m,ITOO(1));
}


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpSetVolume													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun[WmpStream I] I
int wmpSetVolume(mmachine m)
{
	int p,i;
	mywmp r;
	HRESULT hr;
	long volume;
MMechostr(0,"wmp: wmpSetVolume\n");
	i=OTOI(MMpull(m));
	if (i<0) i=0;
	if (i>100) i=100;
	p=MMpull(m);
	if (p==NIL) return MMpush(m,NIL);
	r=(mywmp)MMfetch(m,OTOP(p),0);

	if ((r==NULL)||(r->g_pBA==NULL)) return MMpush(m,NIL);

	//The value corresponds to attenuation in dB 
	//Full volume is 100 , and 0 is silence
	//The scale used by put_volume function is the volume level multiplied by 100 (for example if i==100 dB set -10 000).
	//Modification of volume value to have a linear variation from -10 000 (silence) to 0 (max volume)
	float vol = (float)(i / 100.0);
	if( vol < 0.01 ) volume = -10000;
	else volume = (long)( 5000.0*log10(vol));
	if (S_OK!=(hr=r->g_pBA->put_Volume(volume)))
  {
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not set volume of the filter graph");
		DisplayWmpErrorMessage(hr);
    return MMpush(m,NIL);
  }

	//Update new volume
	r->sVolume.lvolumedB=volume;
	r->sVolume.ivolumelin=i;
	
	//Update new 'mute' state, up/down volume <=> unmute
	r->g_iMute = 0;

	return MMpush(m,0);
}


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpGetVolume													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun[WmpStream] I
int wmpGetVolume(mmachine m)
{
	int p,i;
	mywmp r;
MMechostr(0,"wmp: wmpGetVolume\n");
	p=MMpull(m);
	if (p==NIL) return MMpush(m,NIL);
	r=(mywmp)MMfetch(m,OTOP(p),0);
	if ((r==NULL)) return MMpush(m,NIL);
	
	if (r==NULL) return MMpush(m,NIL);

	//Retrieve the linear volume value
	i=r->sVolume.ivolumelin;

	return MMpush(m,ITOO(i));
}


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpSetMute													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun[WmpStream I] I
int wmpSetMute(mmachine m)
{
	int p,i;
	mywmp r;
	HRESULT hr;
MMechostr(0,"wmp: wmpSetMute\n");
	i=OTOI(MMpull(m));
	p=MMpull(m);
	if (p==NIL) return MMpush(m,NIL);
	r=(mywmp)MMfetch(m,OTOP(p),0);

	if ((r==NULL)||(r->g_pBA==NULL)) return MMpush(m,NIL);
	if (i) 
	{
		hr=r->g_pBA->put_Volume(VOLUME_SILENCE);
		r->g_iMute=1;
	}
	else 
	{
		hr=r->g_pBA->put_Volume(r->sVolume.lvolumedB);
		r->g_iMute=0;
	}
	if (S_OK!=hr)
  {
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not set mute of the filter graph");
		DisplayWmpErrorMessage(hr);
    return MMpush(m,NIL);
   }

	return MMpush(m,0);
}


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpGetMute													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun[WmpStream] I
int wmpGetMute(mmachine m)
{
	int p,i;
	mywmp r;
MMechostr(0,"wmp: wmpGetMute\n");
	p=MMpull(m);
	if (p==NIL) return MMpush(m,NIL);
	r=(mywmp)MMfetch(m,OTOP(p),0);
	if (r==NULL) return MMpush(m,NIL);

	i=r->g_iMute;

	return MMpush(m,ITOO(i));
}

//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpGetPosLength													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun[WmpStream] [I I]
int wmpGetPosLength(mmachine m)
{
	int p;
	mywmp r;
	LONGLONG llcurpos,llstoppos;
	HRESULT hr;
MMechostr(0,"wmp: wmpGetPosLength\n");
	p=MMpull(m);
	if (p==NIL) return MMpush(m,NIL);
	r=(mywmp)MMfetch(m,OTOP(p),0);
	
	if ((r==NULL)||(r->g_pMS==NULL)) return MMpush(m,NIL);

	if (FAILED(hr=r->g_pMS->GetPositions(&llcurpos,&llstoppos)))
  {
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not get current position and stop position of the stream");
		DisplayWmpErrorMessage(hr);
    return MMpush(m,NIL);
  }

	//Conversion from the current time format (100 nanoseconds units) to ms
	llcurpos /= ONE_MILLISECOND;
	llstoppos /= ONE_MILLISECOND;

	//Conversion in ms
	if (MMpush(m,(ITOO((int)llcurpos)))) return MERRMEM;
	if (MMpush(m,(ITOO((int)llstoppos)))) return MERRMEM;
	if (MMpush(m,ITOO(2))) return MERRMEM;

	return MBdeftab(m);
}

 
//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpSetCbImage													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun [WmpStream fun [WmpStream u0] u1 u0] WmpStream
int wmpSetCbImage(mmachine m)
{
MMechostr(0,"wmp: wmpSetCbImage\n");
	return OBJaddreflex(m,typeWMP,RFLWMP_IMAGE);
}


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpSetCbState													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun [WmpStream fun [WmpStream u0 WmpState] u1 u0] WmpStream
int wmpSetCbState(mmachine m)
{
MMechostr(0,"wmp: wmpSetCbState\n");
  return OBJaddreflex(m,typeWMP,RFLWMP_STATE);
}


//Get the state of the media stream (WMP_BUFFERING,WMP_CONTACTING,WMP_ONBEGIN,WMP_ONPAUSE,WMP_ONSTOP,
//WMP_ONCLOSED,WMP_ONOPENED)
int DLGState(mmachine m,HWND h,unsigned msg,UINT id,LONG param,int *ret)
{ 
	int k;
	//$BLG: v4.6a5 - Modif
	//int iState=(int)param;
	int iState;

MMechostr(0,"wmp: DLGState %d\n", param);
  //$BLG: v4.6a5 - Add : We sometimes get undeclared param values, in Scol or in DX SDK evcode.h, such as 16918304 or 17180448 (???)  ...
  //param = param & 0xffff;
//MMechostr(0,"wmp: DLGState %d\n", param);
  iState = (int)param;
  if ((param != WMP_BUFFERING) && (param != WMP_CONTACTING) && (param != WMP_ONBEGIN) && (param != WMP_ONPAUSE) && (param != WMP_ONSTOP) && (param != WMP_ONCLOSED) && (param != WMP_ONOPENED) && (param != WMP_POSITION))
    return 0;

  
	k=OBJbeginreflex(m,typeWMP,id,RFLWMP_STATE);
MMechostr(0,"wmp: %d\n", k);	
	if (k==0)
	{
		if (MMpush(m,iState)) return S_OK;
		if (MMpush(m,ITOO(1))) return S_OK;
		if (k=MBdeftab(m)) return S_OK;
		OBJcallreflex(m,1);
	} 
	return 0;
}

//Blit video sample on a bitmap
int DLGBlitSample (mmachine m,HWND h,unsigned msg,UINT id,LONG param,int *ret)
{ 
MMechostr(0,"wmp: DLGBlitSample\n");

  mywmpbuf m_wmpbuf=(mywmpbuf)param;

/*
  int p;
	p=OBJfindTM(m,typeWMP,id);
  if (p==-1)
  {
    MMechostr(0,"NOOK\n");
  }
  else
  {
  	MMechostr(0,"OK\n");
  }
*/
	
	int k=OBJbeginreflex(m,typeWMP,id,RFLWMP_IMAGE);
	if (k==0) 
	  OBJcallreflex(m,0);
	else
		MMechostr(MSKDEBUG,"\nCan not call reflex blit function");
	// $BLG: v4.6a5 - Moved in wmpBlit() (Line 600)
	//Removed own modif
	m_wmpbuf->msgPosted = false;
	return 0;
}

//Get filters graph event and send the new state of the media stream
int DLGGraphEvent(mmachine m,HWND h,unsigned msg,UINT id,LONG param,int *ret)
{ 
	int iState;
MMechostr(0,"wmp: DLGGraphEvent\n");
	mywmp s=(mywmp) param;

	if ((s==NULL) ||(s->g_pME==NULL)) return 0;
	if (s->buf.m!=m) return 0;

	
  long lEventCode, param1, param2;
  HRESULT hr;
  if (S_OK==(hr = s->g_pME->GetEvent(&lEventCode, &param1, &param2, 0)))
  { 
	  //$BLG: Info source: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/directx9_c_Summer_03/directX/htm/eventnotificationcodes.asp
		//$BLG: All data from a particular stream has been rendered.
		if ((EC_COMPLETE == lEventCode))
	  { 
			iState=WMP_ONCLOSED;
	  } 
		//$BLG: The graph is buffering data, or has stopped buffering data.
		else if ((EC_BUFFERING_DATA == lEventCode))
	  { 
			if (param1)
			{ 
				iState=WMP_BUFFERING;
			}
			else
			{
				iState=WMP_ONBEGIN;
			}
	  } 
		//$BLG: A pause request has completed.
		else if ((EC_PAUSED == lEventCode))
	  {
			iState=WMP_ONPAUSE;
	  } 
		//$BLG: The graph is opening a file, or has finished opening a file.
		else if ((EC_OPENING_FILE == lEventCode))
	  {
			if (param1)
			{ 
				iState=WMP_CONTACTING;
			}
			else
			{
				iState=WMP_ONOPENED;
			}
	  } /*Currently, none of the filters included with DirectShow send this event*/  
	  //$BLG: ??? Certainly speaking about WMP_ONSTOP
	  //$BLG: ??? No default value ?
			
		PostMessage (HScol,WMP_STATE,s->buf.id,(LPARAM)iState);
		hr = s->g_pME->FreeEventParams(lEventCode, param1, param2);
  } 
	else 
	{
		MMechostr(MSKDEBUG,"\nWARNING >>> Could not get event of the filter graph");
		DisplayWmpErrorMessage(hr);
		return 0;
	}

  return 0;
}

//Display DirectShow errors
HRESULT DisplayWmpErrorMessage(HRESULT hr)
{
	char const *pBuffer=NULL;
MMechostr(0,"wmp: DisplayWmpErrorMessage\n");
	//$BLG: v4.6a5 - Modif for WMP cool closure (Moving to DX9 instead of DX8.1 - should be compatible with former version)
	pBuffer=DXGetErrorString8(hr);
	//$BLG: -> DX10
	//pBuffer=DXGetErrorString9(hr);
	//pBuffer=DXGetErrorString(hr);
	if (pBuffer!=NULL) MMechostr(MSKDEBUG,"\nWARNING >>> ERROR =  %s (hr=0x%x)\n", pBuffer,hr);
	return S_OK;
}


#define NWMPPKG 26


char* WMPname[NWMPPKG]=
{
	"WmpState",
	"WMP_BUFFERING",
	"WMP_CONTACTING",
	"WMP_ONBEGIN",
	"WMP_ONPAUSE",
	"WMP_ONSTOP",
	"WMP_ONCLOSED",
	"WMP_ONOPENED",
	"WmpStream",
	"wmpCreate",
	"wmpDestroy",
	"wmpBlit",
	"wmpSetCbImage",
	"wmpSetCbState",
	"wmpPause",
	"wmpPlay",
	"wmpStop",
	"wmpSeek",
	"wmpIsSeekable",
	//"wmpCreateEx",
	"wmpSetVolume",
	"wmpSetMute",
	"wmpGetVolume",
	"wmpGetMute",
	"wmpGetPosLength",
	"wmpCreateFromFile",
	//$LB (07/08/2004)
	"wmpGetSize",

	
};


#define bullshit int (__cdecl *)(struct Mmachine *)
int (*WMPfun[NWMPPKG])(mmachine m)=
{
	NULL,//WmpState
	(bullshit)WMP_BUFFERING,//WMP_BUFFERING
	(bullshit)WMP_CONTACTING,//WMP_CONTACTING
	(bullshit)WMP_ONBEGIN,//WMP_ONBEGIN
	(bullshit)WMP_ONPAUSE,//WMP_ONPAUSE
	(bullshit)WMP_ONSTOP,//WMP_ONSTOP
	(bullshit)WMP_ONCLOSED,//WMP_ONCLOSED
	(bullshit)WMP_ONOPENED,//WMP_ONOPENED
	NULL,//WmpStream
	wmpCreate,//wmpCreate
	wmpDestroy,//wmpDestroy
	wmpBlit,//wmpBlit
	wmpSetCbImage,//wmpSetCbImage
	wmpSetCbState,//wmpSetCbState
	wmpPause,//wmpPause
	wmpPlay,//wmpPlay
	wmpStop,//wmpStop
	wmpSeek,//wmpSeek
	wmpIsSeekable,//wmpIsSeekable
	//wmpCreateEx,//wmpCreateEx
	wmpSetVolume,//wmpSetVolume
	wmpSetMute,//wmpSetMute
	wmpGetVolume,//wmpGetVolume
	wmpGetMute,//wmpGetMute
	wmpGetPosLength,//wmpGetPosLength
	wmpCreateFromFile,//wmpCreateFromFile
	//$LB (07/08/2004)
	wmpGetSize
};


int WMPnarg[NWMPPKG]=
{
	

	TYPTYPE,//WmpState
	TYPCONS0,//WMP_BUFFERING
	TYPCONS0,//WMP_CONTACTING
	TYPCONS0,//WMP_ONBEGIN
	TYPCONS0,//WMP_ONPAUSE
	TYPCONS0,//WMP_ONSTOP
	TYPCONS0,//WMP_ONCLOSED
	TYPCONS0,//WMP_ONOPENED
	TYPTYPE,//WmpStream
	2,//wmpCreate
	1,//wmpDestroy
	2,//wmpBlit
	3,//wmpSetCbImage
	3,//wmpSetCbState
	1,//wmpPause
	1,//wmpPlay
	1,//wmpStop
	2,//wmpSeek
	1,//wmpIsSeekable
	//4,//wmpCreateEx
	2,//wmpSetVolume
	2,//wmpSetMute
	1,//wmpGetVolume
	1,//wmpGetMute
	1,//wmpGetPosLength
	2,//wmpCreateFromFile
	//$LB (07/08/2004)
	1 //wmpGetSize
};


char* WMPtype[NWMPPKG]=
{
	NULL, //WmpState
	"fun [] WmpState",	//WMP_BUFFERING
	"fun [] WmpState",	//WMP_CONTACTING
	"fun [] WmpState",	//WMP_ONBEGIN
	"fun [] WmpState",	//WMP_ONPAUSE	
	"fun [] WmpState",	//WMP_ONSTOP
	"fun [] WmpState",	//WMP_ONCLOSED
	"fun [] WmpState",	//WMP_ONOPENED
	NULL,	//WmpStream
	"fun[Chn S] WmpStream",	//wmpCreate
	"fun[WmpStream] I",	//wmpDestroy
	"fun[ObjBitmap WmpStream] ObjBitmap",	//wmpBlit
	"fun [WmpStream fun [WmpStream u0] u1 u0] WmpStream",	//wmpSetCbImage
	"fun [WmpStream fun [WmpStream u0 WmpState] u1 u0] WmpStream",	//wmpSetCbState
	"fun[WmpStream] I",	//wmpPause
	"fun[WmpStream] I",	//wmpPlay
	"fun[WmpStream] I",	//wmpStop
	"fun[WmpStream I] I",	//wmpSeek
	"fun[WmpStream] I",	//wmpIsSeekable
	//"fun[Chn S S S] WmpStream",	//wmpCreateEx
	"fun[WmpStream I] I",	//wmpSetVolume
	"fun[WmpStream I] I",	//wmpSetMute
	"fun[WmpStream] I",	//wmpGetVolume
	"fun[WmpStream] I",	//wmpGetMute
	"fun[WmpStream] [I I]",	//wmpGetPosLength
	"fun[Chn P] WmpStream",	//wmpCreateFromFile
	//$LB (07/08/2004)
	"fun [WmpStream] [I I]" //wmpGetSize
};


int SCOLloadWMP(mmachine m,cbmachine w)
//extern "C" __declspec (dllexport) int SCOLloadWMP(mmachine m,cbmachine w)
{
	int k;

	ww=w;
//MMechostr(0,"wmp: SCOLloadWMP\n");
	//Events to send to the main window for activation of video renderer blit callback
	//only way against problem due to DirectShow using his own thread for receiving of streaming samples
	WMP_BLIT=OBJgetUserEvent();
	OBJdefEvent(WMP_BLIT,DLGBlitSample);

	//Event to get media stream state
	WMP_STATE=OBJgetUserEvent();
	OBJdefEvent(WMP_STATE,DLGState);

	//Event to get filters graph events
	WM_GRAPHNOTIFY=OBJgetUserEvent();
	OBJdefEvent(WM_GRAPHNOTIFY,DLGGraphEvent);

	WMP_ID=248;

	k=PKhardpak(m,"WMP-1.0.pkg",NWMPPKG,WMPname,WMPfun,WMPnarg,WMPtype);
	typeWMP=OBJregister(RFLWMP_NB,0,wmpDestroy2,"OBJTYPEWMP");
	return k;
}

