#include "StdAfx.h"
#include <jni.h>
#include <dshow.h>
#include <D3d9.h>
#include <Vmr9.h>
#include <Dvdmedia.h>
#include "JStreamReader.h"




CReaderPin::CReaderPin(CBaseFilter *pFilter,CCritSec *pLockIn,HRESULT *phr,DSMediaDecodingPlayer *inMdp):
CBasePin((TCHAR *)NAME("CReaderPin"),pFilter,pLockIn,phr,(TCHAR *)NAME("CReaderPin"),PINDIR_OUTPUT){
	pLock=pLockIn;
	aSyncReaderIQueried=FALSE;
	mdp=inMdp;
	/*
	mt=new CMediaType();
	mt->InitMediaType();
	mt->SetVariableSize();
	mt->SetType(&MEDIATYPE_Stream);
	mt->SetSubtype(&MEDIASUBTYPE_Avi);
	*/
	refs=0;
	for(int i=0;i<MAX_TYPES;i++){
		CMediaType* mt=new CMediaType();
		mt->InitMediaType();
		mt->SetVariableSize();
		mt->SetType(&MEDIATYPE_Stream);
		mt->SetSubtype(&SUBTYPEGUIDS[i]);
		mediaTypes[i]=mt;
	}

}



STDMETHODIMP CReaderPin::NonDelegatingQueryInterface(REFIID riid, void **ppi){
	CheckPointer(ppi,E_POINTER);
	if (riid == IID_IAsyncReader){
		aSyncReaderIQueried=TRUE;
		//

		HRESULT hr=GetInterface((IAsyncReader *)this, ppi);
		//printf("Requested (nondelegating) IAsyncReader %d\n",hr);
		//fflush(stdout);
		return hr;
	}
	//else{

	HRESULT hr=CBasePin::NonDelegatingQueryInterface(riid,ppi);
	//printf("Query nd interface %d\n",hr);
	return hr;
	//}
}

HRESULT STDMETHODCALLTYPE CReaderPin::QueryInterface(REFIID refId,void **ppi){

	if(ppi==NULL){
		return E_POINTER;
	}
	if(refId==IID_IAsyncReader){
		aSyncReaderIQueried=TRUE;

		HRESULT hr=GetInterface((IAsyncReader*)this,ppi);
		//printf("Requested IAsyncReader %d\n",hr);
		//fflush(stdout);
		return hr;
	}
	/*
	if(refId==IID_IAMovieSetup){
	return GetInterface((IAMovieSetup*)this,ppi);
	}
	if(refId==IID_IMediaFilter){
	return GetInterface((IMediaFilter*)this,ppi);
	}
	if(refId==IID_IBaseFilter){
	return GetInterface((IBaseFilter*)this,ppi);
	}
	*/
	HRESULT hr=CBasePin::QueryInterface(refId,ppi);
	//printf("Query interface %d\n",hr);
	return hr;
}

ULONG STDMETHODCALLTYPE CReaderPin::AddRef(){
	return refs++;
}
ULONG STDMETHODCALLTYPE CReaderPin::Release(){
	return refs--;
}

HRESULT CReaderPin::CheckMediaType(const CMediaType * mt){
	const GUID* mmt=mt->Type();
	const GUID *fmtType=mt->FormatType();
	printf("Format type : %s\n",GuidNames[*fmtType]);
	if(*fmtType==FORMAT_VideoInfo2){
		VIDEOINFOHEADER2* vi = (VIDEOINFOHEADER2*) mt->pbFormat;
		LONG w=vi->bmiHeader.biWidth;
		LONG h=vi->bmiHeader.biHeight;

		//printf("%ld x %ld\n",w,h);
		VIDEOINFOHEADER2* vic=(VIDEOINFOHEADER2*)malloc(sizeof(VIDEOINFOHEADER2));
		memcpy(vic,vi,sizeof(VIDEOINFOHEADER2));
		mdp->setVideoSize(mt->subtype,w,h);
	}else if(*fmtType==FORMAT_VideoInfo){
		VIDEOINFOHEADER* vi = (VIDEOINFOHEADER*) mt->pbFormat;
		LONG w=vi->bmiHeader.biWidth;
		LONG h=vi->bmiHeader.biHeight;

		//printf("%ld x %ld\n",w,h);
		VIDEOINFOHEADER *vic=(VIDEOINFOHEADER*)malloc(sizeof(VIDEOINFOHEADER));
		memcpy(vic,vi,sizeof(VIDEOINFOHEADER));
		mdp->setVideoSize(mt->subtype,w,h);
	}
	//printf("Check media type major format: %s\n",GuidNames[mt->majortype]);
	//printf("Check media sub format: %s\n",GuidNames[mt->subtype]);

	if(mt->majortype==MEDIATYPE_Stream){
		for(int i=0;i<MAX_TYPES;i++){
			//if(mt->subtype==MEDIASUBTYPE_Avi || mt->subtype==MEDIASUBTYPE_MPEG1System){
			//
			//}
			if(mt->subtype==mediaTypes[i]->subtype){
				//printf("Media type OK\n");

				return S_OK;
			}
		}
	}

	return S_FALSE;
}
HRESULT CReaderPin::GetMediaType(int pos,CMediaType * pmt){
	if(pos<0)return E_INVALIDARG;
	if(pos>=MAX_TYPES){
		return VFW_S_NO_MORE_ITEMS;
	}
	*pmt=*mediaTypes[pos];
	return S_OK;
}



HRESULT CReaderPin::CheckConnect(IPin *pPin){
	PIN_INFO pinInfo;
	pPin->QueryPinInfo(&pinInfo);
	IBaseFilter* f=pinInfo.pFilter;
	FILTER_INFO fInfo;
	f->QueryFilterInfo(&fInfo);

	printf("Connect req  pin %ls of filter %ls\n",pinInfo.achName,fInfo.achName);

	//bool connectOk=aSyncReaderIQueried;
	bool connectOk=TRUE;
	// reset 
	aSyncReaderIQueried=FALSE;
	if(connectOk){


		pPin->QueryInterface(IID_IMemInputPin,(void **)&memInPin);
		pPin->AddRef();

		HRESULT hr= CBasePin::CheckConnect(pPin);
		return hr;
	}
	fflush(stdout);
	return S_FALSE;

}

STDMETHODIMP CReaderPin::Connect (IPin *pReceivePin,const AM_MEDIA_TYPE *pmt)
{
	/*
	if(pmt!=NULL){
	printf("Connect major format: %s\n",GuidNames[pmt->majortype]);
	printf("Connect sub format: %s\n",GuidNames[pmt->subtype]);
	}
	*/
	HRESULT hr=CBasePin::Connect(pReceivePin, pmt);
	if(hr==VFW_E_ALREADY_CONNECTED){
		printf("VFW_E_ALREADY_CONNECTED\n");
	}else if(hr==VFW_E_NO_ACCEPTABLE_TYPES){
		printf("VFW_E_NO_ACCEPTABLE_TYPES\n");
	}else if(hr==VFW_E_NOT_STOPPED){
		printf("VFW_E_NOT_STOPPED\n");
	}else if(hr==VFW_E_TYPE_NOT_ACCEPTED){
		printf("VFW_E_TYPE_NOT_ACCEPTED\n");
	}

	return hr;

}
STDMETHODIMP CReaderPin::ReceiveConnect (IPin *pReceivePin,const AM_MEDIA_TYPE *pmt)
{
	/*
	if(pmt!=NULL){
	printf("Connect major format: %s\n",GuidNames[pmt->majortype]);
	printf("Connect sub format: %s\n",GuidNames[pmt->subtype]);
	}
	*/
	HRESULT hr=CBasePin::Connect(pReceivePin, pmt);
	return hr;

}

HRESULT CReaderPin::BreakConnect(){

	// reset 
	aSyncReaderIQueried=FALSE;
	if(memInPin!=NULL){
		memInPin->Release();
	}
	return CBasePin::BreakConnect();

}


HRESULT CReaderPin::DecideBufferSize(
									 IMemAllocator *pAlloc,
									 ALLOCATOR_PROPERTIES *ppropInputRequest
									 ){
										 ALLOCATOR_PROPERTIES actProps;
										 ALLOCATOR_PROPERTIES iMProps;

										 if(pAlloc!=NULL){
											 if(ppropInputRequest!=NULL){

												 pAlloc->SetProperties(ppropInputRequest,&actProps);
											 }

											 pAlloc->GetProperties(&iMProps);

										 }	
										 return S_OK;
}


HRESULT CReaderPin::CompleteConnect(IPin *pReceivePin){
	//if (aSyncReaderIQueried){
	PIN_INFO pinInfo;
	pReceivePin->QueryPinInfo(&pinInfo);
	IBaseFilter* f=pinInfo.pFilter;
	FILTER_INFO fInfo;
	f->QueryFilterInfo(&fInfo);

	//printf("Complete connected pin %ls of filter %ls\n",pinInfo.achName,fInfo.achName);
	//fflush(stdout);
	return CBasePin::CompleteConnect (pReceivePin);
	//}else{
	//	return VFW_E_NO_TRANSPORT;
	//}

}
HRESULT CReaderPin::GetDeliveryBuffer(
									  IMediaSample **ppSample,
									  REFERENCE_TIME *pStartTime,
									  REFERENCE_TIME *pEndTime,
									  DWORD dwFlags
									  ){
										  return S_OK;
}
/*
HRESULT CReaderPin::Deliver(IMediaSample *iSample){

long length =iSample->GetActualDataLength();
printf("Deliver\n");
fflush(stdout);
return CBasePin::Deliver(iSample);
}
*/
HRESULT STDMETHODCALLTYPE CReaderPin::BeginFlush(void){
	return mdp->PinBeginFlush();
}
HRESULT STDMETHODCALLTYPE CReaderPin::EndFlush(void){
	HRESULT hr=mdp->PinEndFlush();
	return hr;

}




HRESULT STDMETHODCALLTYPE CReaderPin::RequestAllocator(IMemAllocator *piMemAlloc,ALLOCATOR_PROPERTIES *props,IMemAllocator **ppIalloc){
	// accept the preferred for now
	ALLOCATOR_PROPERTIES iMProps;
	ALLOCATOR_PROPERTIES actProps;

	HRESULT hr=S_OK;
	ALLOCATOR_PROPERTIES sProps;

	//printf("Requested alloc props buffers %d len %d prefix: %d align: %d\n",props->cBuffers,props->cbBuffer,props->cbPrefix,props->cbAlign);
	if(props!=NULL){
		sProps.cBuffers=props->cBuffers;
		sProps.cbPrefix=props->cbPrefix;
		sProps.cbBuffer=props->cbBuffer;
		int align=props->cbAlign;
		if(align==0){
			// select alignment
			// "must be an even power of two"
			align=2048;
		}
		sProps.cbAlign=align;
	}

	if(piMemAlloc!=NULL){
		piMemAlloc->GetProperties(&iMProps);
		//printf("Preferred alignment %d\n",iMProps.cbAlign);
		if(props!=NULL){

			hr=piMemAlloc->SetProperties(&sProps,&actProps);
			//printf("Buffers %d\n",actProps.cBuffers);
		}
		if(hr==S_OK){
			piMemAlloc->GetProperties(&iMProps);
			if(iMProps.cBuffers>0){
				//printf("Buffers %d\n",iMProps.cBuffers);
				piMemAlloc->AddRef();
				*ppIalloc=piMemAlloc;
			}else{
				hr=-1;
			}
		}
	}
	if(piMemAlloc==NULL || hr!=S_OK){
		//HRESULT hr;
		IMemAllocator *pAlloc;
		hr = CoCreateInstance(CLSID_MemoryAllocator, NULL, 
			CLSCTX_INPROC_SERVER, IID_IMemAllocator, (void **)&pAlloc);

		if(hr==S_OK){
			hr=pAlloc->SetProperties(&sProps,&actProps);	
			if(hr==VFW_E_ALREADY_COMMITTED){
				printf("VFW_E_ALREADY_COMMITTED\n");
			}else if(hr==VFW_E_BADALIGN){
				printf("VFW_E_BADALIGN\n");
			}else if(hr==VFW_E_BUFFERS_OUTSTANDING){
				printf("VFW_E_BUFFERS_OUTSTANDING\n");
			}
			*ppIalloc=pAlloc;
			//printf("Buffers %d\n",actProps.cBuffers);
		}

	}
	return hr;
}


void CReaderPin::MediaSampleToBuffer(IMediaSample *pSample,LONGLONG *pPosition,LONG *pLength,BYTE **ppBuf){
	pSample->GetPointer(ppBuf);
	// time <-> byte position conversion, see DirectShow docu (The IAsyncReader interface is the worst interface ever seen !)
	REFERENCE_TIME start;
	REFERENCE_TIME stop;
	pSample->GetTime(&start,&stop);
	LONGLONG pos=start/10000000;
	LONGLONG stopPos=stop/10000000;
	*pLength=(LONG)(stopPos-pos);
	*pPosition=pos;
}

HRESULT STDMETHODCALLTYPE CReaderPin::Request(IMediaSample *pMs,DWORD_PTR dwUser){
	LONGLONG position;
	LONG length;
	BYTE *buf;
	MediaSampleToBuffer(pMs,&position,&length,&buf);
	HRESULT res=mdp->request(pMs,position,length,buf,dwUser);
	return res;
}

HRESULT STDMETHODCALLTYPE CReaderPin::WaitForNext(DWORD dwTimeOut,IMediaSample **ppSample,DWORD_PTR *pdwUser){
	return mdp->WaitForNext(dwTimeOut,ppSample,pdwUser);
}

HRESULT STDMETHODCALLTYPE CReaderPin::SyncReadAligned(IMediaSample *pSample){
	long len=pSample->GetActualDataLength();
	BYTE *buf;
	pSample->GetPointer(&buf);
	// time <-> byte position conversion, see DirectShow docu (The IAsyncReader interface is the worst interface ever seen !)
	REFERENCE_TIME start;
	REFERENCE_TIME stop;
	pSample->GetTime(&start,&stop);
	LONGLONG position=start/10000000;
	LONGLONG stopPos=stop/10000000;
	LONGLONG length=stopPos-position;
	HRESULT hr=mdp->read(position,(LONG)length,buf);

	return hr;
}
HRESULT STDMETHODCALLTYPE CReaderPin::SyncRead(LONGLONG position,LONG length,BYTE *buf){
	HRESULT res=mdp->read(position,length,buf);
	return res;
}
HRESULT STDMETHODCALLTYPE CReaderPin::Length(LONGLONG *pTotal,LONGLONG *pAvailable){
	HRESULT res=mdp->length(pTotal,pAvailable);
	return res;
}

CReaderPin::~CReaderPin(void){
}


CJStreamReader::CJStreamReader(LPUNKNOWN pUnk,HRESULT *phr,CCritSec* pLock,DSMediaDecodingPlayer *inMdp) :
CBaseFilter(NAME("JStreamReader"),pUnk,pLock,CLSID_CJStreamReader,phr)
{
	mdp=inMdp;
	refs=0;
	outPin=new CReaderPin(this,pLock,phr,mdp);

}

int CJStreamReader::GetPinCount(){
	return 1;
}
CBasePin* CJStreamReader::GetPin(int n){
	if(n==0){
		return outPin;
	}else{
		return NULL;
	}
}
CJStreamReader::~CJStreamReader(void)
{

}
