#include "StdAfx.h"
#include <streams.h>
#include <jni.h>
#include <dshow.h>
#include <Mmreg.h>
#include "JAudioRenderer.h"

JNIEXPORT jobject JNICALL Java_ips_media_jnm_impl_directshow_JAudioRenderer_init(JNIEnv *env, jobject obj){
	JavaVM *javaVm;
	env->GetJavaVM(&javaVm);
	// weak (can be garbage collected if object is gone) ref to Java object
	jweak objRef=env->NewWeakGlobalRef(obj);

	jclass exceptionCls=env->FindClass("ips/media/NativeMediaSystemException");
	if(exceptionCls==NULL){
		printf("Could not find exception class\n");
		return NULL;
	}
	//exceptionClassRef=env->NewWeakGlobalRef(exceptionCls);
	jclass cls=env->GetObjectClass(objRef);
	if(cls==NULL){
		printf("Could not find class\n");
		return NULL;
	}
	jmethodID writeMid = env->GetMethodID(cls, "audioWrite", "([B)I");
	if (writeMid == 0){
		printf("Could not find method ID\n");
		return NULL;
	}


	HRESULT hr;

	JAudioRenderer* pJar=NULL;
	JAudioRenderer::CreateInstance(javaVm, objRef, writeMid, NULL, &hr,&pJar);
	//unsigned int nObjSize=sizeof(DSMediaDecodingPlayer);
	unsigned int nObjSize=0;
	return env->NewDirectByteBuffer(pJar,nObjSize);
}

JAudioRenderer::JAudioRenderer(JavaVM* jVm,jweak oRef,jmethodID wMid,LPUNKNOWN pUnk,HRESULT *phr) :
CBaseRenderer(CLSID_JAudioRenderer,NAME("JAudioRenderer"), pUnk,phr)
{
	javaVm=jVm;
	objRef=oRef;
	writeMid=wMid;
	renderThreadID=0;
	renderEnv=NULL;
}

HRESULT JAudioRenderer::CreateInstance(JavaVM* jVm, jweak oRef, jmethodID wMid, LPUNKNOWN pUnk, HRESULT *phr, JAudioRenderer **ppJar)
{
	if (ppJar == NULL)
	{
		return E_POINTER;
	}

	JAudioRenderer *pJar = new (std::nothrow) JAudioRenderer(jVm,oRef,wMid,pUnk, phr);
	if (pJar == NULL)
	{
		return E_OUTOFMEMORY;
	}
	*ppJar = pJar;
	pJar->AddRef();
	return S_OK;
}


// CheckMediaType
//
// Check if the pin can support this specific proposed type and format
//
HRESULT JAudioRenderer::CheckMediaType(const CMediaType *mediaType)
{
	//const GUID *formatType=mediaType->FormatType();
	//printf("Major format: %s\n",GuidNames[mediaType->majortype]);
	//printf("Sub format: %s\n",GuidNames[mediaType->subtype]);

	if(mediaType->majortype == MEDIATYPE_Audio){
		if(mediaType->subtype == MEDIASUBTYPE_PCM){
			
			return S_OK;
		}
		//printf("Audio format subtype: %s not supported\n",GuidNames[mediaType->subtype]);
		//fflush(stderr);
		return S_FALSE;
	}else {
		//printf(stderr, "Audio renderer: Media type: %s not supported\n", GuidNames[mediaType->majortype]);
		//fflush(stdout);
		return S_FALSE;
	}

}

HRESULT JAudioRenderer::SetMediaType(const CMediaType *pmt)   {   	
	const GUID *fmtType=pmt->FormatType();
	//printf("Format type : %s\n",GuidNames[*fmtType]);
	//jboolean res=false;
	if(*fmtType==FORMAT_WaveFormatEx){
		WAVEFORMATEX* wvFormat=(WAVEFORMATEX*)(pmt->Format());
		//if(wvFormat->wFormatTag==WAVE_FORMAT_EXTENSIBLE){
		jint chs=wvFormat->nChannels;
		jint sampleRate=wvFormat->nSamplesPerSec;
		jint bitsPerSample=wvFormat->wBitsPerSample;

		//printf("Audio format: chs: %d, sampleRate: %d, bitsPerSample %d\n",chs,sampleRate,bitsPerSample);

		JNIEnv* env;
		jint res=javaVm->AttachCurrentThread((void**)&env,NULL);
		if(res==JNI_OK){
			jclass cls=env->GetObjectClass(objRef);
			if(cls!=NULL){
				jmethodID mid=env->GetMethodID(cls,"setAudioFormat","(III)V");
				if(mid!=0){
					env->CallVoidMethod(objRef,mid,sampleRate,bitsPerSample,chs);
				}
			}
			javaVm->DetachCurrentThread();

		}
	}
	return S_OK;
}   

HRESULT JAudioRenderer::Active(){
	//printf("Audio open\n");
	jboolean openRes=JNI_FALSE;
	JNIEnv* env;
	jint res=javaVm->AttachCurrentThread((void**)&env,NULL);
	if(res==JNI_OK){
		jclass cls=env->GetObjectClass(objRef);
		if(cls!=NULL){
			jmethodID mid=env->GetMethodID(cls,"open","()Z");
			if(mid!=0){
				openRes=env->CallBooleanMethod(objRef,mid);
			}
		}
		javaVm->DetachCurrentThread();
	}
	if(openRes){
		return CBaseRenderer::Active();
	}else{
		return S_FALSE;
	}
}
HRESULT JAudioRenderer::OnStartStreaming(){
	JNIEnv* env;
	jint res=javaVm->AttachCurrentThread((void**)&env,NULL);
	if(res==JNI_OK){
		jclass cls=env->GetObjectClass(objRef);
		if(cls!=NULL){
			jmethodID mid=env->GetMethodID(cls,"start","()V");
			if(mid!=0){
				env->CallVoidMethod(objRef,mid);

			}
		}
		javaVm->DetachCurrentThread();
	}
	return S_OK;
}
HRESULT JAudioRenderer::DoRenderSample(IMediaSample *pMediaSample){
	//printf("doRenderSample\n");
	//fflush(stdout);
	long dataLength=pMediaSample->GetActualDataLength();

	BYTE *pBuf;
	HRESULT hr=pMediaSample->GetPointer(&pBuf);
	AM_MEDIA_TYPE *mType=NULL;

	pMediaSample->GetMediaType(&mType);

	JNIEnv *env;
	jint res=javaVm->AttachCurrentThread((void **)&env,NULL);

	if(res==JNI_OK){
		jbyteArray buf=env->NewByteArray(dataLength);
		env->SetByteArrayRegion(buf,0,dataLength,(jbyte *)pBuf);
		env->CallVoidMethod(objRef,writeMid,buf);
		env->DeleteLocalRef(buf);

		javaVm->DetachCurrentThread();
	}
	return S_OK;
}

HRESULT JAudioRenderer::BeginFlush(){
	HRESULT superHr=CBaseRenderer::BeginFlush();
	//printf("Audio flush\n");
	return superHr;
}
HRESULT JAudioRenderer::OnStopStreaming(){
	JNIEnv* env;
	jint res=javaVm->AttachCurrentThread((void**)&env,NULL);
	if(res==JNI_OK){
		jclass cls=env->GetObjectClass(objRef);
		if(cls!=NULL){
			jmethodID mid=env->GetMethodID(cls,"stop","()V");
			if(mid!=0){
				env->CallVoidMethod(objRef,mid);
			}
		}
		javaVm->DetachCurrentThread();
	}
	return S_OK;
}
HRESULT JAudioRenderer::Inactive(){
	//printf("Audio close\n");
	JNIEnv* env;
	jint res=javaVm->AttachCurrentThread((void**)&env,NULL);
	if(res==JNI_OK){
		jclass cls=env->GetObjectClass(objRef);
		if(cls!=NULL){
			jmethodID mid=env->GetMethodID(cls,"close","()V");
			if(mid!=0){
				env->CallVoidMethod(objRef,mid);
				return CBaseRenderer::Inactive();
			}
		}
		javaVm->DetachCurrentThread();
	}
	return S_FALSE;
}

JAudioRenderer::~JAudioRenderer(void)
{
}
