/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
////																					////
////																					////
////							 - 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>
#include <d3dx8.h>
#include <Dxerr8.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;

	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;
	
	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";

	// 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;
	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
	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)
{

	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;
	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);
}


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpDestroy													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun[WmpStream] I
int wmpDestroy(mmachine m)
{
	int p;

    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 ;

    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;
	int i,j,kx,x,ky,y,p,i0;
	unsigned char colR, colG, colB;
	mywmp r; 
	//$LB
	OBJBITMAP_BUFFER s;

	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;
	kx=(r->buf.width<<8)/rb.nbcolumn;
	ky=(r->buf.height<<8)/rb.nblines;

	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
			_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; 

			x+=kx;
		}
		y+=ky;
		//$LB
		s+=rb.nextline;
	}


	return 0;



}


//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpPause													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun[WmpStream] I
int wmpPause(mmachine m)
{	

	int p;
	mywmp r;
	HRESULT hr;


	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;
	


	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;
    
	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;

	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;

	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;

	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;

	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;

	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;



	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;
	


	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)
{

	return OBJaddreflex(m,typeWMP,RFLWMP_IMAGE);

}

//////////////////////////////////////////////////////////////////////////////////////////////
///		wmpSetCbState													
//////////////////////////////////////////////////////////////////////////////////////////////
////fun [WmpStream fun [WmpStream u0 WmpState] u1 u0] WmpStream
int wmpSetCbState(mmachine m)
{

   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;
	int iState=(int)param;


	k=OBJbeginreflex(m,typeWMP,id,RFLWMP_STATE);
	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)

{ 
	mywmpbuf m_wmpbuf=(mywmpbuf)param;

	int k=OBJbeginreflex(m,typeWMP,id,RFLWMP_IMAGE);
	if (k==0) OBJcallreflex(m,0);
	else
		MMechostr(MSKDEBUG,"\nCan not call reflex blit function");
	
	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;
	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)))
    { 
	
		if ((EC_COMPLETE == lEventCode))
        { 
			iState=WMP_ONCLOSED;
      
        } 
		else if ((EC_BUFFERING_DATA == lEventCode))
        { 
			if (param1)
			{ 
				iState=WMP_BUFFERING;

			}
			else
			{
				iState=WMP_ONBEGIN;

			}
      
        } 
		else if ((EC_PAUSED == lEventCode))
        { 
			
			iState=WMP_ONPAUSE;
      
        } 
		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*/
		
		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;

	pBuffer=DXGetErrorString8(hr);
	if (pBuffer!=NULL) MMechostr(MSKDEBUG,"\nWARNING >>> ERROR =  %s (hr=0x%x)\n", pBuffer,hr);
	return S_OK;

}

#define NWMPPKG 25


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"

	
};

#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

};


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
};


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


};

int SCOLloadWMP(mmachine m,cbmachine w)
//extern "C" __declspec (dllexport) int SCOLloadWMP(mmachine m,cbmachine w)
{
	int k;

	ww=w;

	//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;
}

