package test;

import java.io.File;
import java.text.MessageFormat;
import java.util.stream.Stream;

import com.sun.jna.Platform;

import anywheresoftware.b4a.keywords.DateTime;
import bass.BassLibrary;
import bass.BassMixLibrary;
import bass.BassLibrary.BASS_DEVICEINFO;
import bass.BassLibrary.BASS_FILEPROCS;

@SuppressWarnings("all")
public class testbass {
	static long recordtimestamp = 0;
	static long dsptimestamp = 0;
	
	public static void testbass() {
		search_for_existing_library();
		
		get_versions();
		
		scan_playback_devices();
		//test_playback_fileproc(1);
		//test_mixer(1);
		//test_playback_streamproc(1);
		
		scan_recording_devices();
		//test_recording(0);
	}
	
	private static void test_playback_fileproc(int devid) {
		if (BassLibrary.BASS.BASS_Init(devid, 8000, 0)) {
			raise_log("Init success for for device "+devid);
		} else bass_error("BASS_Init device "+devid);
		
		BASS_FILEPROCS bf = new BASS_FILEPROCS(
				// filecloseproc
				(user)->{
					raise_log("FILECLOSEPROC called");
				},
				// filelenproc
				(user)->{
					raise_log("FILELENPROC called");
					return 0;
				},
				// filereadproc
				(buffer, length, user)->{
					raise_log("FILEREADPROC called");
					return 0;
				},
				// fileseekproc
				(offset, user)->{
					raise_log("FILESEEKPROC called");
					return false;
				}
				);
		
		int handle = BassLibrary.BASS.BASS_StreamCreateFileUser(BassLibrary.Constant.STREAMFILE_BUFFER, 0, bf, null);
		
		if (handle!=0) {
			raise_log("handle = "+handle);
			if (BassLibrary.BASS.BASS_StreamFree(handle))
				raise_log("Stream freed");
			else bass_error("BASS_StreamFree");
		} else bass_error("BASS_StreamCreateFileUser");
		
		if (BassLibrary.BASS.BASS_Free()) {
			raise_log("Playback freed");
		} else bass_error("BASS_Free");
	}
	
	private static void test_mixer(int devid) {
		if (BassLibrary.BASS.BASS_Init(devid, 8000, 0)) {
			raise_log("Init success for device "+devid);
		} else bass_error("BASS_Init device "+devid);
		
		int mixhandle = BassMixLibrary.BASSMIX.BASS_Mixer_StreamCreate(8000, 1, 0);
		if (mixhandle!=0) {
			raise_log("Mixer created, handle = "+mixhandle);
			
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				raise_log("Mixer Interrupted");
			}
			
			if (BassLibrary.BASS.BASS_StreamFree(mixhandle)) {
				raise_log("Mixer freed");
			} else bass_error("BASS_StreamFree");
			
		} else bass_error("BASS_Mixer_StreamCreate");
		if (BassLibrary.BASS.BASS_Free()) {
			raise_log("Playback freed");
		} else bass_error("BASS_Free");
	}
	
	private static void test_playback_streamproc(int devid) {
		if (BassLibrary.BASS.BASS_Init(devid, 8000, 0)) {
			raise_log("Init success for device "+devid);
		} else bass_error("BASS_Init device "+devid);
		
		int handle = BassLibrary.BASS.BASS_StreamCreate(8000, 1, 0, (hh, buffer, length, user)->{
			raise_log(MessageFormat.format("STREAMPROC triggered, handle={0}, length={1}", hh,length));
			return 0;
		}, null);
		
		
		if (handle!=0) {
			raise_log("StreamCreate handle = "+handle);
			
			int dsphandle = BassLibrary.BASS.BASS_ChannelSetDSP(handle, (dsph, hh, buffer, length, user )->{
				long delta = DateTime.getNow() - dsptimestamp;
				dsptimestamp = DateTime.getNow();
				raise_log(MessageFormat.format("DSP triggered, handle={0}, dsphandle={1}, length={2}, interval={3} ms", hh, dsph, length, delta));
			}, null, 0);
			
			if (dsphandle!=0) {
				dsptimestamp = DateTime.getNow();
				raise_log("DSP created, handle = "+dsphandle);
			} else bass_error("BASS_ChannelSetDSP");
			
			if (BassLibrary.BASS.BASS_ChannelStart(handle)) {
				raise_log("BASS_ChannelStart success");
			} else bass_error("BASS_ChannelStart");
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				raise_log("STREAMPROC PUSH Interrupt Exception");
			}
			
			if (dsphandle!=0) {
				if (BassLibrary.BASS.BASS_ChannelRemoveDSP(handle, dsphandle)) {
					raise_log("DSP Removed");
				} else bass_error("BASS_ChannelRemoveDSP");
			}
			
			if (BassLibrary.BASS.BASS_StreamFree(handle)) {
				raise_log("handle freed");
			} else bass_error("BASS_STREAMFREE STREAMPROCPUSH");
			
			if (BassLibrary.BASS.BASS_Free()) {
				raise_log("Playback freed");
			} else bass_error("BASS_FREE");
		} else bass_error("BASS_StreamCreate STREAMPROCPUSH");
	}
	
	private static void test_playback_streamproc_push(int devid) {
		if (BassLibrary.BASS.BASS_Init(devid, 8000, 0)) {
			raise_log("Init success for device "+devid);
		} else bass_error("BASS_Init device "+devid);
		
		int handle = BassLibrary.BASS.BASS_StreamCreate(8000, 1, 0, BassLibrary.Constant.STREAMPROC_PUSH, null);
		if (handle!=0) {
			raise_log("StreamCreate handle = "+handle);
			
			int dsphandle = BassLibrary.BASS.BASS_ChannelSetDSP(handle, (dsph, hh, buffer, length, user )->{
				long delta = DateTime.getNow() - dsptimestamp;
				dsptimestamp = DateTime.getNow();
				raise_log(MessageFormat.format("DSP triggered, handle={0}, dsphandle={1}, length={2}, interval={3} ms", hh, dsph, length, delta));
			}, null, 0);
			
			if (dsphandle!=0) {
				dsptimestamp = DateTime.getNow();
				raise_log("DSP created, handle = "+dsphandle);
			} else bass_error("BASS_ChannelSetDSP");
			
			if (BassLibrary.BASS.BASS_ChannelPlay(handle,true)) {
				raise_log("BASS_ChannelStart success");
			} else bass_error("BASS_ChannelStart");
			
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				raise_log("STREAMPROC PUSH Interrupt Exception");
			}
			
			if (dsphandle!=0) {
				if (BassLibrary.BASS.BASS_ChannelRemoveDSP(handle, dsphandle)) {
					raise_log("DSP Removed");
				} else bass_error("BASS_ChannelRemoveDSP");
			}
			
			if (BassLibrary.BASS.BASS_StreamFree(handle)) {
				raise_log("handle freed");
			} else bass_error("BASS_STREAMFREE STREAMPROCPUSH");
			
			if (BassLibrary.BASS.BASS_Free()) {
				raise_log("Playback freed");
			} else bass_error("BASS_FREE");
		} else bass_error("BASS_StreamCreate STREAMPROCPUSH");
	}
	
	private static void search_for_existing_library() {
		System.out.println("Resource Prefix = "+Platform.RESOURCE_PREFIX);
		String[] classpath = System.getProperty("java.class.path").split(";");
		//System.out.println("Class path = ");
		//Stream.of(classpath).forEach(cp -> System.out.println(cp));
		String jnalibpath = System.getProperty("jna.library.path");
		System.out.println("JNA library path = "+jnalibpath);
		String[] javalibpath = System.getProperty("java.library.path").split(";");
		
		
		System.out.println("Searching for BASS Library at Java Library Path : ");
		Stream.of(javalibpath).forEach(jp -> {
			File bb = new File(jp,System.mapLibraryName("bass"));
			if (bb.exists()) {
				System.out.println("Found "+bb.getAbsolutePath());
			}
			File bc = new File(jp, System.mapLibraryName("bassmix"));
			if (bc.exists()) {
				System.out.println("Found "+bc.getAbsolutePath());
			}
		});
	}
	
	private static void get_versions() {
		int v1 = BassLibrary.BASS.BASS_GetVersion();
		System.out.println("Bass Version="+Integer.toHexString(v1));
		int v2 = BassMixLibrary.BASSMIX.BASS_Mixer_GetVersion();
		System.out.println("BassMix Version="+Integer.toHexString(v2));
	}
	
	private static void scan_playback_devices() {
		int ii = 0;
		while(true) {
			BASS_DEVICEINFO di = new BASS_DEVICEINFO();
			if (BassLibrary.BASS.BASS_GetDeviceInfo(ii, di)) {
				raise_log("BASS_GetDeviceInfo index="+ii+" success");
				raise_log("DeviceInfo = "+di.toString());
				ii++;
			} else {
				bass_error("BASS_GetDeviceInfo ii="+ii);
				break;
			}
		}
	}
	
	private static void scan_recording_devices() {
		int ii = 0;
		while(true) {
			BASS_DEVICEINFO di = new BASS_DEVICEINFO();
			if (BassLibrary.BASS.BASS_RecordGetDeviceInfo(ii, di)) {
				raise_log("BASS_RecordGetDeviceInfo index="+ii+" success");
				raise_log("DeviceInfo = "+di.toString());
				ii++;
			} else {
				bass_error("BASS_RecordGetDeviceInfo ii="+ii);
				break;
			}
		}
		
	}
	
	private static void test_recording(int devid) {
		if (BassLibrary.BASS.BASS_RecordInit(devid)) {
			raise_log(MessageFormat.format("BASS_RecordInit device {0} success",devid));
			
			// pake cara biasa
//			int rechandle = BassLibrary.BASS.BASS_RecordStart(8000, 1, BassLibrary.BassConstant.BASS_RECORD_PAUSE, new RECORDPROC() {
//
//				@Override
//				public boolean RECORDPROC(int handle, Pointer buffer, int length, Pointer user) {
//					long delta = DateTime.getNow() - recordtimestamp;
//					recordtimestamp = DateTime.getNow();
//					raise_log(MessageFormat.format("RECORDPROC triggered, handle={0}, length={1}, Interval={2} ms", handle, length, delta));
//					return true;
//				}
//				
//			}, null);
			
			// pakai lambda
			int rechandle = BassLibrary.BASS.BASS_RecordStart(8000, 1, BassLibrary.Constant.BASS_RECORD_PAUSE, (handle, buffer, length, user)->{
				long delta = DateTime.getNow() - recordtimestamp;
				recordtimestamp = DateTime.getNow();
				raise_log(MessageFormat.format("RECORDPROC triggered, handle={0}, length={1}, Interval={2} ms", handle, length, delta));
				return true;
			}, null);
			raise_log(MessageFormat.format("Record Handle = {0}", rechandle));
			
			if (rechandle!=0) {
				if (BassLibrary.BASS.BASS_ChannelStart(rechandle)) {
					raise_log("Recording Started");
					recordtimestamp = DateTime.getNow();
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						raise_log("Recording Sleep exception = "+e.getMessage());
					}
					
				} else bass_error("BASS_ChannelStart recordhandle");
				
				if (BassLibrary.BASS.BASS_ChannelFree(rechandle)) {
					raise_log("Recording Stopped");
				} else bass_error("BASS_ChannelStop recordhandle");
				
				if (BassLibrary.BASS.BASS_RecordFree()) {
					raise_log("Recording channel freed");
				} else bass_error("BASS_RecordFree");
			} else bass_error("BASS_RecordStart");
		} else bass_error("BASS_RecordInit 0");
	}
	
	private static void raise_log(String msg) {
		System.out.println(msg);
	}
	private static void bass_error(String functionname) {
		System.out.println("BASS error on function "+functionname+", Code="+BassLibrary.BASS.BASS_ErrorGetCode());
	}
	
	
}
