package code;

import com.sun.jna.Memory;
import com.sun.jna.Pointer;

import bass.BassLibrary;
import bass.BassMixLibrary;
import net.sourceforge.peers.Logger;
import net.sourceforge.peers.media.SoundSource;

/**
 * Pengganti /AxisAudio/src/net/sourceforge/peers/media/FileReader.java
 * karena terbatas codec nya
 * @author rdkartono
 *
 */
public class BassFileReader implements SoundSource {
	private Logger logger;
	// Device = 0 --> no sound
	private final int bassdev = 0;
	// Device BassInit normal 48khz
	private final int initfreq = 48000;
	// Device BassInit set ke mono, 16 bit
	private final int initflag = BassLibrary.Constant.BASS_DEVICE_MONO | BassLibrary.Constant.BASS_DEVICE_16BITS;
	// flag ketika open file, BASS_STREAM_DECODE karena akan plug ke mixer
	private final int openflag = BassLibrary.Constant.BASS_STREAM_DECODE;
	// Mixer frequency untuk convert ke 8khz
	private final int mixerfreq = 8000;	
	// Mixer channel untuk convert ke Mono
	private final int mixerchannel = 1; 
	// Mixer create flag BASS_STREAM_DECODE karena untuk ambil data aja, bukan playback
	private final int mixerflag = BassLibrary.Constant.BASS_STREAM_DECODE;
	// Mixer_AddChannel flag BAS_STREAM_AUTOFREE , supaya kalau channel habis & selesai, channel free sendiri
	//private final int addchannelflag = BassLibrary.Constant.BASS_STREAM_AUTOFREE;
	private final int addchannelflag = 0;
	
	// sekali baca data, 256 bytes
	private final int BUFFER_SIZE = 256;
	
	private final String filename;
	private int filehandle = 0;
	private int mixerhandle = 0;
	private long filesize = 0;
	private double duration = 0;
	// indicator if file opened, mixer created, and file plugged to mixer
	private boolean channeladded = false;
	
	private BassFileReaderListener listener;	// belum kepake
	
	public BassFileReader(final String filename, BassFileReaderListener listener) {
		this.filename = filename;
		this.listener = listener;
		if (BassLibrary.BASS.BASS_GetVersion()!=0) {
			if (BassMixLibrary.BASSMIX.BASS_Mixer_GetVersion()!=0) {
				boolean initresult = BassLibrary.BASS.BASS_Init(bassdev, initfreq, initflag);
				if (initresult) {
					System.out.println("BassFileReader Bass_Init device = "+bassdev+" success");
				} else System.out.println("BassFileReader BASS_Init error = "+BassLibrary.BASS.GetBassError("BASS_Init"));
				
				filehandle = BassLibrary.BASS.BASS_StreamCreateFile(false, filename, 0, 0, openflag);
				if (filehandle!=0){
					filesize = BassLibrary.BASS.BASS_ChannelGetLength(filehandle, BassLibrary.Constant.BASS_POS_BYTE);
					duration = BassLibrary.BASS.BASS_ChannelBytes2Seconds(filehandle, filesize);
					if (listener!=null) listener.fileopened(filename, filesize, duration);
					
					mixerhandle = BassMixLibrary.BASSMIX.BASS_Mixer_StreamCreate(mixerfreq, mixerchannel, mixerflag);
					if (mixerhandle!=0) {
						if (BassMixLibrary.BASSMIX.BASS_Mixer_StreamAddChannel(mixerhandle, filehandle, addchannelflag)) {
							// sequence pertama sukses
							if (listener!=null) listener.filereadstart(filename);
							channeladded = true;
						} else {
							logger_error("BassFileReader BASS_Mixer_StreamAddChannel error = "+BassLibrary.BASS.GetBassError("BASS_Mixer_StreamAddChannel"));
							close();
						}
					} else {
						logger_error("BassFileReader BASS_Mixer_StreamCreate error = "+BassLibrary.BASS.GetBassError("BASS_Mixer_StreamCreate"));
						close();
					}
				} else logger_error("BassFileReader BASS_StreamCreateFile error = "+BassLibrary.BASS.GetBassError("BASS_StreamCreateFile"));
			} else logger_error("BassFileReader BASS_Mixer_GetVersion invalid");
		} else logger_error("BassFileReader BASS_GetVersion invalid");
	}
	
	public synchronized void close() {
		if (mixerhandle!=0) {
			if (BassLibrary.BASS.BASS_StreamFree(mixerhandle)) {
				logger_info("mixer closed in BassFileReader");
			} else logger_error("BassFileReader mixerhandle BASS_StreamFree error = "+BassLibrary.BASS.GetBassError("BASS_StreamFree"));
			mixerhandle = 0;
		}
		if (filehandle!=0) {
			if (BassLibrary.BASS.BASS_StreamFree(filehandle)) {
				logger_info(filename+" closed in BassFileReader");
			} else logger_error("BassFileReader filehandle BASS_StreamFree error = "+BassLibrary.BASS.GetBassError("BASS_StreamFree"));
			filehandle = 0;
		} 
		
		if (listener!=null) listener.fileclosed(filename);
		channeladded = false;
	}
	
	@Override
	public synchronized byte[] readData() {
		if (channeladded) {
			Pointer pbuf = new Memory(BUFFER_SIZE);
			int readsize = BassLibrary.BASS.BASS_ChannelGetData(mixerhandle, pbuf, BUFFER_SIZE);
			if (readsize>0) {
				byte[] rr = pbuf.getByteArray(0, readsize);
				if (listener!=null) listener.filereadresult(filename, readsize, rr);
				try {
					Thread.sleep(15);
					return rr;
				} catch (InterruptedException e) {
					logger_error("BassFileReader interrupted");
				}
			} else if (readsize==-1) {
				// ada error
				logger_error("BassFileReader BASS_ChannelGetData error = "+BassLibrary.BASS.GetBassError("BASS_ChannelGetData"));
			} else {
				// selesai
				if (listener!=null) listener.filereadfinish(filename);
				logger_info("BassFileReader BASS_ChannelGetData readsize = "+readsize+", closing it");
				close();
			}
		}
		return null;
	}
	
	public void AddListener(BassFileReaderListener listener) {
		this.listener = listener;
	}

	private void logger_error(String msg) {
		if (logger!=null) 
			logger.error(msg);
		else System.out.println(msg);
	}
	
	private void logger_info(String msg) {
		if (logger!=null) 
			logger.info(msg);
		else System.out.println(msg);
	}
	
}
