package code;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.text.MessageFormat;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;

import anywheresoftware.b4a.BA;
import lombok.Getter;
import net.sourceforge.peers.Config;
import net.sourceforge.peers.Logger;
import net.sourceforge.peers.media.MediaManager;
import net.sourceforge.peers.media.MediaMode;
import net.sourceforge.peers.sip.Utils;
import net.sourceforge.peers.sip.core.useragent.SipListener;
import net.sourceforge.peers.sip.core.useragent.UserAgent;
import net.sourceforge.peers.sip.syntaxencoding.SipHeaders;
import net.sourceforge.peers.sip.syntaxencoding.SipURI;
import net.sourceforge.peers.sip.syntaxencoding.SipUriSyntaxException;
import net.sourceforge.peers.sip.transactionuser.Dialog;
import net.sourceforge.peers.sip.transactionuser.DialogManager;
import net.sourceforge.peers.sip.transport.SipRequest;
import net.sourceforge.peers.sip.transport.SipResponse;
import test.JavaSipEvents;

@BA.ShortName("jSIPClient")
@BA.Events(values= {
		"log(msg as string)",
		"registering(req as SIP_Request)",
		"registersuccesfull(resp as SIP_Response)",
		"registerfailed(resp as SIP_Response)",
		"incomingcall(req as SIP_Request, resp as SIP_Response)",
		"remotehangup(req as SIP_Request)",
		"ringing(resp as SIP_Response)",
		"calleepickup(resp as SIP_Response)",
		"soundchannelopened",
		"soundchannelclosed",
		"inputchanneldevicefailure",
		"inputchannelbuffering(msg as string)",
		"inputchannelpcmdata(length as int, bb() as byte)",
		"inputchannellevel(value as int)",
		"outputchanneldevicefailure",
		"outputchannelbuffering(msg as string)",
		"outputchannelpcmdata(length as int, bb() as byte)",
		"outputchannellevel(value as int)"
})
public class jSIPClient {
	
	public jSIPClient() {
		gs = new GsonBuilder()
				.setPrettyPrinting()
				.create();
		runningfolder = new File("").getAbsolutePath();
		
		Runtime.getRuntime().addShutdownHook(new Thread() {
			
			public void run() {
				raise_log_event("jSIPClient ShutdownHook called");
				save_config();
				HangUp();
				Disconnect();
			}
		});
	}
	
	private BA ba;
	private String event;
	private Object Me = this;
	public CustomConfig cc = new CustomConfig();
	private EventManager em;
	private Gson gs;
	private final String runningfolder;
	
	//events boolean
	private boolean need_log_event = false;
	private boolean need_registering_event = false;
	private boolean need_registersuccesfull_event = false;
	private boolean need_registerfailed_event = false;
	private boolean need_incomingcall_event = false;
	private boolean need_remotehangup_event = false;
	private boolean need_ringing_event = false;
	private boolean need_calleepickup_event = false;
	private boolean need_soundchannelopened_event = false;
	private boolean need_soundchannelclosed_event = false;
	private boolean need_inputchanneldevicefailure_event = false;
	private boolean need_inputchannelbuffering_event =false;
	private boolean need_inputchannelpcmdata_event = false;
	private boolean need_inputchannellevel_event = false;
	private boolean need_outputchanneldevicefailure_event = false;
	private boolean need_outputchannelbuffering_event = false;
	private boolean need_outputchannelpcmdata_event = false;
	private boolean need_outputchannellevel_event = false;
	
	private JavaSipEvents jse;
	private BassFileReaderListener bfrl;
	
	private ExecutorService exec = Executors.newSingleThreadExecutor();
	
	/**
	 * Initialize jSIPClient
	 * @param eventname Event name
	 */
	public void Initialize(BA ba, String eventname) {
		this.ba = ba;
		this.event = eventname;
		load_config();
		check_events();
	}
	
	/**
	 * Check if Initialize already called
	 * @return true if initialized
	 */
	public boolean IsInitialized() {
		if (ba!=null) {
			if (common.ValidString(event)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Connect to SIP Server 
	 * @param serverip SIP Server IP
	 * @param username Username to register
	 * @param password Password to register
	 * @param localip use if need to specify which Network Adapter to use
	 * @return true if parameter completed
	 */
	public boolean Connect(String serverip, String username, String password, String localip) {
		Disconnect();
		if (cc==null) {
			System.out.println("Creating Custom Config");
			cc = new CustomConfig();
		} else System.out.println("Custom Config already created");
		if (common.ValidString(serverip)) cc.setDomain(serverip);
		if (common.ValidString(username)) cc.setUserPart(username);
		if (common.ValidString(password)) cc.setPassword(password);
		if (common.ValidString(localip)) cc.SetLocalInetAddress(localip);
		em = new EventManager(cc);
		
		return false;
	}
	
	@BA.Hide
	public void SetJavaSipEvent(JavaSipEvents xx) {
		this.jse = xx;
	}
	
	@BA.Hide
	public void SetBassFileReaderListener(BassFileReaderListener xx) {
		this.bfrl = xx;
	}
	
	/**
	 * Set Input Volume
	 * @param value 0 - 100
	 */
	public void setInputVolume(int value) {
		if (em!=null) em.setInputVolume(value);
	}
	
	/**
	 * Get Input Volume
	 * @return 0 - 100
	 */
	public int getInputVolume() {
		if (em!=null) {
			return em.getInputVolume();
		} else return 0;
	}
	
	/**
	 * Set Output Volume
	 * @param value 0 - 100
	 */
	public void setOutputVolume(int value) {
		if (em!=null) em.setOutputVolume(value);
	}
	
	
	/**
	 * Get Output Volum
	 * @return 0 - 100
	 */
	public int getOutputVolume() {
		if (em!=null) {
			return em.getOutputVolume();
		} else return 0;
	}
	
	/**
	 * Disconnect from SIP Server
	 */
	public void Disconnect() {
		if (em!=null) {
			em.Close();
			em = null;
		}
	}
	
	/**
	 * HangUp current call
	 * @return true if success
	 */
	public boolean HangUp() {
		if (em!=null) {
			if (em.IsCreated()) {
				if (em.CallingInProgress()) {
					em.HangUp();
					return true;
				} else raise_log_event("HangUp failed, no call made");
			} else raise_log_event("HangUp failed, EventManager not created");
		} else raise_log_event("HangUp failed, Connect first");
		return false;
	}
	
	/**
	 * Stream an audiofile to somebody
	 * @param target target to call
	 * @param filename file to stream
	 * @return true if can be streamed
	 */
	public boolean StreamFile(String target, String filename) {
		if (em!=null) {
			if (em.IsCreated()) {
				if (cc!=null) {
					cc.setMediaFile(filename);
					cc.setMediaMode(MediaMode.file);
					em.Call(target);
					em.AddListener(bfrl);
					return true;
				} else raise_log_event("StreamFile failed, Custom Config not created");
			} else raise_log_event("StreamFile failed, EventManager not created");
		} else raise_log_event("StreamFile failed, Connect first");
		return false;
	}
	
	/**
	 * Call somebody
	 * @param target target to call
	 * @return true if can be called
	 */
	public boolean Call(String target) {
		if (em!=null) {
			if (em.IsCreated()) {
				if (cc!=null) {
					cc.setMediaMode(MediaMode.captureAndPlayback);
					em.Call(target);
					return true;
				} else raise_log_event("Call failed, Custom Config not created");
			} else raise_log_event("Call failed, EventManager not created");
		} else raise_log_event("Call failed, Connect first");
		return false;
	}
	
	/**
	 * Accept Incoming Call
	 * @param req SIP_Request object
	 * @return true if call can be accepted
	 */
	public boolean AcceptIncomingCall(SIP_Request req) {
		if (em!=null) {
			if (em.IsCreated()) {
				if (req!=null) {
					cc.setMediaMode(MediaMode.captureAndPlayback);
					em.AcceptCall(req.getReq());
					return true;
				} else raise_log_event("AcceptIncomingCall failed, SIP_Request is null");
			} else raise_log_event("AcceptIncomingCall failed, EventManager not created");
		} else raise_log_event("AcceptIncomingCall failed, Connect first");
		return false;
	}
	
	/**
	 * Send DTMF code
	 * @param code code to send
	 * @return true if can be send
	 */
	public boolean SendDTMF(final char code) {
		if (em!=null) {
			if (em.IsCreated()) {
				em.DTMF(code);
				return true;
			} else raise_log_event("SendDTMF failed, EventManager not created");
		} else raise_log_event("SendDTMF failed, Connect first");
		return false;
	}
	
	/**
	 * Reject Incoming Call
	 * @param req SIP_Request to reject
	 * @return true if can be rejected
	 */
	public boolean RejectIncomingCall(SIP_Request req) {
		if (em!=null) {
			if (em.IsCreated()) {
				if (req!=null) {
					em.RejectCall(req.getReq());
					return true;
				} else raise_log_event("RejectIncomingCall failed, SIP_Request is null");
			} else raise_log_event("RejectIncomingCall failed, EventManager not created");
		} else raise_log_event("RejectIncomingCall failed, Connect first");
		return false;
	}
	
	
	
	/////////// Class Implementation ////////////////////
	
	public class EventManager implements SipListener{
		private UserAgent user_agent;
		private SipRequest sip_request;
		private boolean all_success = false;
		//JavaxSoundManager jsm;
		BassSoundManager jsm;
		
		public EventManager(final CustomConfig xx) {
			all_success = false;
			//Logger lg = new FileLogger(null);
			Logger lg = null;
//			jsm = new JavaxSoundManager(false, lg, null);
			jsm = new BassSoundManager(1,0, new BassSoundManagerListener() {

				@Override
				public void Opened() {
					raise_log_event("BassSoundManager Opened");
					raise_soundchannelopened_event();
				}

				@Override
				public void Closed() {
					raise_log_event("BassSoundManager Closed");
					raise_soundchannelclosed_event();
				}

				@Override
				public void log_info(String msg) {
					raise_log_event("BassSoundManager info : "+msg);
				}

				@Override
				public void log_error(String msg) {
					raise_log_event("BassSoundManager error : "+msg);
					
				}

				@Override
				public void WriteFromRTP(int length, byte[] pcmdata) {
					// buat debugging aja, kalau dikeluarin, spamming log
					//raise_log_event("BassSoundmanager WriteFromRTP "+length+" bytes");
					raise_outputchannelpcmdata_event(length, pcmdata);
				}

				@Override
				public void ReadToRTP(int length, byte[] pcmdata) {
					// buat debugging aja, kalau dikeluarin, spamming log
					//raise_log_event("BassSoundManager ReadToRTP "+length+" bytes");
					raise_inputchannelpcmdata_event(length, pcmdata);
				}

				@Override
				public void DeviceFailure(String msg) {
					raise_log_event("BassSondManager DeviceFailure : "+msg);
					if (msg.contains("OutputChannel"))
						raise_outputchanneldevicefailure_event();
					else if (msg.contains("InputChannel"))
						raise_inputchanneldevicefailure_event();
				}

				@Override
				public void BufferingInformation(String msg) {
					raise_log_event("BassSoundManager BufferingInformation : "+msg);
					if (msg.contains("OutputChannel"))
						raise_outputchannelbuffering_event(msg);
					else if (msg.contains("OutputChannel"))
						raise_inputchannelbuffering_event(msg);
				}

				@Override
				public void ChannelLevel(boolean is_input, int value) {
					if (is_input) 
						raise_inputchannellevel_event(value);
					else
						raise_outputchannellevel_event(value);
					
				}
				
			});
			
			try {
				user_agent = new UserAgent(this, xx, lg, jsm);
				exec.submit(new Runnable() {

					@Override
					public void run() {
						try {
							user_agent.register();
							all_success = true;
						} catch (SipUriSyntaxException e) {
							raise_log_event("EventManager register exception = "+e.getMessage());
						}
					}
					
				});
				
			} catch (SocketException e) {
				raise_log_event("EventManager create exception = "+e.getMessage());
			}
			
		}
		
		public void setInputVolume(int value) {
			if (jsm!=null) jsm.setInputChannelVolume(value);
		}
		
		public int getInputVolume() {
			if (jsm!=null) {
				return jsm.getInputChannelVolume();
			} else return 0;
		}
		
		public void setOutputVolume(int value) {
			if (jsm!=null) jsm.setOutputChannelVolume(value);
		}
		
		public int getOutputVolume() {
			if (jsm!=null) {
				return jsm.getOutputChannelVolume();
			} else return 0;
		}
		
		/**
		 * Check if EventManager succesfully created
		 * @return true if success
		 */
		public boolean IsCreated() {
			return all_success;
		}
		
		/**
		 * Call 
		 * @param targetsip Target SIP
		 */
		public void Call(final String targetsip) {
			if (user_agent!=null) {
				exec.submit(new Runnable() {

					@Override
					public void run() {
						sip_request = null;
						try {
							sip_request = user_agent.invite(targetsip, null);
						} catch (SipUriSyntaxException e) {
							raise_log_event("Call exception = "+e.getMessage());
						}
					}
					
				});
				
			}
		}
		
		/**
		 * Accept Incoming Call
		 * @param req SipRequest to accept
		 */
		public void AcceptCall(final SipRequest req) {
			if (user_agent!=null) {
				if (req!=null) {
					exec.submit(new Runnable() {

						@Override
						public void run() {
							// source : https://github.com/ymartineau/peers/blob/master/peers-gui/src/main/java/net/sourceforge/peers/gui/EventManager.java
							String callId = Utils.getMessageCallId(req);
			                DialogManager dialogManager = user_agent.getDialogManager();
			                Dialog dialog = dialogManager.getDialog(callId);
			                user_agent.acceptCall(req, dialog);
						}
						
					});
					
				}
			}
		}
		
		/**
		 * Add Listener for File Streaming
		 * @param event BassFileReaderListener
		 * @return true if can be added
		 */
		public boolean AddListener(BassFileReaderListener event) {
			if (user_agent!=null) {
				MediaManager mm = user_agent.getMediaManager();
				if (mm!=null) {
					BassFileReader bfr = mm.getFileReader();
					if (bfr!=null) {
						bfr.AddListener(event);
						return true;
					}
				}
			}
			return false;
		}
		
		
		
		public void DTMF(final char code) {
			if (user_agent!=null) {
				exec.submit(new Runnable() {

					@Override
					public void run() {
						MediaManager mm = user_agent.getMediaManager();
						mm.sendDtmf(code);
					}
					
				});
				
				
			}
		}
		
		/**
		 * Reject Incoming Call
		 * @param req SipRequest to reject
		 */
		public void RejectCall(final SipRequest req) {
			if (user_agent!=null) {
				if (req!=null) {
					exec.submit(new Runnable() {

						@Override
						public void run() {
							user_agent.rejectCall(req);
							
						}
						
					});
					
				}
			}
		}
		
		/**
		 * Check if Calling in Progress
		 * @return true if in progress
		 */
		public boolean CallingInProgress() {
			return sip_request!=null;
		}
		
		/**
		 * Hang-Up current call
		 */
		public void HangUp() {
			if (sip_request!=null) {
				if (user_agent!=null) {
					exec.submit(new Runnable() {

						@Override
						public void run() {
							user_agent.terminate(sip_request);
							sip_request = null;
						}
						
					});
					
				} else raise_log_event("HangUp failed, user_agent not available");
			} else raise_log_event("HangUp failed, sip_request not available");
		}
		
		/**
		 * Close Connection to SIP Server
		 */
		public void Close() {
			if (user_agent!=null) {
				user_agent.close();
			}
		}
		
		@BA.Hide
		@Override
		public void registering(SipRequest sipRequest) {
			SIP_Request req = new SIP_Request(sipRequest);
			raise_registering_event(req);
		}

		@BA.Hide
		@Override
		public void registerSuccessful(SipResponse sipResponse) {
			SIP_Response resp = new SIP_Response(sipResponse);
			raise_registersuccesfull_event(resp);
		}

		@BA.Hide
		@Override
		public void registerFailed(SipResponse sipResponse) {
			SIP_Response resp = new SIP_Response(sipResponse);
			raise_registerfailed_event(resp);
		}

		@BA.Hide
		@Override
		public void incomingCall(SipRequest sipRequest, SipResponse provResponse) {
			SIP_Request req = new SIP_Request(sipRequest);
			SIP_Response resp = new SIP_Response(provResponse);
			raise_incomingcall_event(req, resp);
		}

		@BA.Hide
		@Override
		public void remoteHangup(SipRequest sipRequest) {
			SIP_Request req = new SIP_Request(sipRequest);
			raise_remotehangup_event(req);
		}

		@BA.Hide
		@Override
		public void ringing(SipResponse sipResponse) {
			SIP_Response resp = new SIP_Response(sipResponse);
			raise_ringing_event(resp);
		}

		@BA.Hide
		@Override
		public void calleePickup(SipResponse sipResponse) {
			SIP_Response resp = new SIP_Response(sipResponse);
			raise_calleepickup_event(resp);
		}

		@BA.Hide
		@Override
		public void error(SipResponse sipResponse) {
			raise_log_event("sip error, response="+gs.toJson(sipResponse));
			
		}
		
	}
	
	@BA.ShortName("SIP_Request")
	public class SIP_Request{
		private @Getter final SipRequest req;
		public String To;
		public String From;
		public String CallID;
		public String Contact;

		public SipRequest getReq() {
            return req;
            }
		
		public SIP_Request(SipRequest source) {
			req = source;
			if (req!=null) {
				SipHeaders head = source.getSipHeaders();
				this.To = common.GetSIPHeaderValue(head,"To");
				this.From = common.GetSIPHeaderValue(head, "From");
				this.CallID = common.GetSIPHeaderValue(head, "Call-ID");
				this.Contact = common.GetSIPHeaderValue(head, "Contact");
			}
		}
		
		@Override
		public String toString() {
			return MessageFormat.format("To:{0}\n"
					+ "From:{1}\n"
					+ "Call-ID:{2}\n"
					+ "Contact:{3}", To, From, CallID, Contact);
		}
	}
	
	@BA.ShortName("SIP_Response")
	public class SIP_Response{
		public SIP_Response(SipResponse source) {
			resp = source;
			if (resp!=null) {
				this.statusCode = source.getStatusCode();
				this.reasonPhrase = source.getReasonPhrase();
				SipHeaders head = source.getSipHeaders();
				this.To = common.GetSIPHeaderValue(head, "To");
				this.From = common.GetSIPHeaderValue(head, "From");
				this.CallID = common.GetSIPHeaderValue(head, "Call-ID");
				this.Contact = common.GetSIPHeaderValue(head, "Contact");
				this.Server = common.GetSIPHeaderValue(head, "Server");
				this.Date = common.GetSIPHeaderValue(head, "Date");
			}
		}
		private @Getter final SipResponse resp;
		
		public int statusCode;
		public String reasonPhrase;
		public String From;
		public String To;
		public String CallID;
		public String Server;
		public String Contact;
		public String Date;
		
		@Override
		public String toString() {
			return MessageFormat.format("To:{0}\n"
					+ "From:{1}\n"
					+ "Call-ID:{2}\n"
					+ "Contact:{3}\n"
					+ "Server:{4}\n"
					+ "Date:{5}\n"
					+ "StatusCode:{6}\n"
					+ "ReasonPhrase:{7}", To, From, CallID, Contact, Server, Date, statusCode, reasonPhrase);
		}
	}
	
	public class CustomConfig implements Config{
		private InetAddress localIpAddress;
		private InetAddress publicIpAddress;
		private String registrar;
		private String username;
		private String password;
		private int sip_port;
		private int rtp_port;
		private transient MediaMode mm;			// tidak disave dan load
		private SipURI outbondproxy;
		private transient boolean mediadebug;	// tidak disave dan load
		private transient String mediafile;		// tidak disave dan load
		
		
		
		/**
		 * Create CustomConfig
		 * @param registrar Server IP
		 * @param username username
		 * @param password password
		 */
		public CustomConfig(String registrar, String username, String password) {
			this.registrar = registrar;
			this.password = password;
			this.username = username;
			setLocalInetAddress(null);
			
		}
		
		/**
		 * Create CustomConfig
		 * @param registrar Server IP
		 * @param username username
		 * @param password password
		 * @param localip local ethernet to use to register. if not valid, will use default LocalHost
		 */
		public CustomConfig(String registrar, String username, String password, String localip) {
			this.registrar = registrar;
			this.password = password;
			this.username = username;
			SetLocalInetAddress(localip);
			
		}
		
		public CustomConfig() {
			this.registrar = "192.168.5.1";
			this.username = "1002";
			this.password = "1234";
			setLocalInetAddress(null);
		}

		/**
		 * Get Local IP Address
		 */
		@Override
		public InetAddress getLocalInetAddress() {
			return localIpAddress;
			
		}
		
		/**
		 * Get public IP Address used
		 */
		@Override
		public InetAddress getPublicInetAddress() {
			return publicIpAddress;
		}

		/**
		 * Get Username to use for registering with SIP Server
		 */
		@Override
		public String getUserPart() {
			return username;
		}

		/**
		 * Get SIP Server (registrar)
		 */
		@Override
		public String getDomain() {
			return registrar;
		}

		/**
		 * Get Password
		 */
		@Override
		public String getPassword() {
			return password;
		}

		/**
		 * Get Outbond Proxy used
		 */
		@Override
		public SipURI getOutboundProxy() {
			return outbondproxy;
		}

		/**
		 * Get SIP Port . default is 5060
		 */
		@Override
		public int getSipPort() {
			return sip_port;
		}

		/**
		 * Get Current MediaMode
		 */
		@Override
		public MediaMode getMediaMode() {
			return mm;
		}

		/**
		 * Not used
		 */
		@Override
		public boolean isMediaDebug() {
			return mediadebug;
		}

		/**
		 * Get MediaFile used
		 */
		@Override
		public String getMediaFile() {
			return mediafile;
		}

		/**
		 * Get RTP Port used
		 */
		@Override
		public int getRtpPort() {
			return rtp_port;
		}

		/**
		 * Set Local Network Interface to use with jSIPClient
		 * useful when having multiple network interfaces in the machine
		 */
		@Override
		public void setLocalInetAddress(InetAddress inetAddress) {
			try {
				this.localIpAddress = inetAddress == null ? InetAddress.getLocalHost() : inetAddress;
			} catch (UnknownHostException e) {
				raise_log_event("setLocalInetAddress exception = "+e.getMessage());
			}
			
		}
		
		/**
		 * Set Local Network Interface to use with jSIPClient
		 * useful when having multiple network interfaces in the machine
		 * @param ipaddress IP Address of the network interface to use in the machine
		 */
		public void SetLocalInetAddress(String ipaddress) {
			try {
				InetAddress xx = InetAddress.getByName(ipaddress);
				setLocalInetAddress(xx);
			} catch (UnknownHostException e) {
				raise_log_event("SetLocalInetAddress exception = "+e.getMessage());
			}
		}

		/**
		 * Set Public InetAddress to use with JSIPClient
		 * personal note : dont know if really necessary
		 */
		@Override
		public void setPublicInetAddress(InetAddress inetAddress) {
			publicIpAddress = inetAddress;
		}
		
		/**
		 * Set Public IP Address to use with jSIPClient
		 * Personal Note : dont know if really necessary
		 * @param ipaddress IP Address in string
		 */
		public void SetPublicInetAddress(String ipaddress) {
			try {
				InetAddress xx = InetAddress.getByName(ipaddress);
				setPublicInetAddress(xx);
			} catch (UnknownHostException e) {
				raise_log_event("SetPublicInetAddress exception = "+e.getMessage());
			}
		}

		/**
		 * Set Username to use for registering to SIP Server
		 */
		@Override
		public void setUserPart(String username) {
			this.username = username;
			
		}

		/**
		 * Set SIP Server (registrar) IP Address / Domain
		 */
		@Override
		public void setDomain(String domain) {
			registrar = domain;
			
		}

		/**
		 * Set Password to use for registering to SIP Server
		 */
		@Override
		public void setPassword(String password) {
			this.password = password;
			
		}

		/**
		 * Not used
		 */
		@Override
		public void setOutboundProxy(SipURI outboundProxy) {
			outbondproxy = outboundProxy;
			
		}

		/**
		 * Not used
		 */
		@Override
		public void setSipPort(int sipPort) {
			this.sip_port = common.ValidPortNumber(sipPort) ? sipPort : 5060;
			
		}

		/**
		 * Not used
		 */
		@Override
		public void setMediaMode(MediaMode mediaMode) {
			mm = mediaMode;
			
		}

		/**
		 * Not used
		 */
		@Override
		public void setMediaDebug(boolean mediaDebug) {
			mediadebug = mediaDebug;
			
		}

		/**
		 * Not used
		 */
		@Override
		public void setMediaFile(String mediaFile) {
			mediafile = mediaFile;
			
		}

		/**
		 * Not used
		 */
		@Override
		public void setRtpPort(int rtpPort) {
			rtp_port = common.ValidPortNumber(rtpPort) ? rtpPort : 49152 ;
			
		}

		/**
		 * Not used
		 */
		@Override
		public void save() {}
		
	}
	
	
	////////////  private functions ///////////////
	
	/**
	 * Check for events initialization
	 */
	private void check_events() {
		if (IsInitialized()) {
			need_log_event = ba.subExists(event+"_log");
			need_registering_event = ba.subExists(event+"_registering");
			need_registersuccesfull_event = ba.subExists(event+"_registersuccesfull");
			need_registerfailed_event = ba.subExists(event+"_registerfailed");
			need_incomingcall_event = ba.subExists(event+"_incomingcall");
			need_remotehangup_event = ba.subExists(event+"_remotehangup");
			need_ringing_event = ba.subExists(event+"_ringing");
			need_calleepickup_event = ba.subExists(event+"_calleepickup");
			need_inputchanneldevicefailure_event = ba.subExists(event+"_inputchanneldevicefailure");
			need_inputchannelbuffering_event = ba.subExists(event+"_inputchannelbuffering");
			need_inputchannelpcmdata_event = ba.subExists(event+"_inputchannelpcmdata");
			need_outputchanneldevicefailure_event = ba.subExists(event+"_outputchanneldevicefailure");
			need_outputchannelbuffering_event = ba.subExists(event+"_outputchannelbuffering");
			need_outputchannelpcmdata_event = ba.subExists(event+"_outputchannelpcmdata");
			need_soundchannelclosed_event = ba.subExists(event+"_soundchannelclosed");
			need_soundchannelopened_event = ba.subExists(event+"_soundchannelopened");
			need_inputchannellevel_event = ba.subExists(event+"_inputchannellevel");
			need_outputchannellevel_event = ba.subExists(event+"_outputchannellevel");
		}
	}
	
	private void raise_log_event(String msg) {
		if (need_log_event) 
			ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg});
		else 
			System.out.println(msg);
	}
	
	private void raise_registering_event(SIP_Request req) {
		if (need_registering_event) ba.raiseEventFromDifferentThread(Me, null , 0, event+"_registering", false, new Object[] {req});
		if (jse!=null) jse.Registering(req);
	}
	
	private void raise_registersuccesfull_event(SIP_Response resp) {
		if (need_registersuccesfull_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_registersuccesfull", false, new Object[] {resp});
		if (jse!=null) jse.RegisterSuccesful(resp);
	}
	
	private void raise_registerfailed_event(SIP_Response resp) {
		if (need_registerfailed_event) ba.raiseEventFromDifferentThread(Me, null, 0,event+"_registerfailed", false, new Object[] {resp});
		if (jse!=null) jse.RegisterFailed(resp);
	}
	
	private void raise_incomingcall_event(SIP_Request req, SIP_Response resp) {
		if (need_incomingcall_event) ba.raiseEventFromDifferentThread(Me, null, 0,event+"_incomingcall", false, new Object[] {req, resp});
		if (jse!=null) jse.IncomingCall(req, resp);
	}
	
	private void raise_remotehangup_event(SIP_Request req) {
		if (need_remotehangup_event) ba.raiseEventFromDifferentThread(Me, null, 0,  event+"_remotehangup", false, new Object[] {req});
		if (jse!=null) jse.RemoteHangUp(req);
	}
	
	private void raise_ringing_event(SIP_Response resp) {
		if (need_ringing_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_ringing", false, new Object[] {resp});
		if (jse!=null) jse.Ringing(resp);
	}
	
	private void raise_calleepickup_event(SIP_Response resp) {
		if (need_calleepickup_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_calleepickup", false, new Object[] {resp});
		if (jse!=null) jse.CalleePickup(resp);
	}
	
	private void raise_inputchanneldevicefailure_event(){
		if (need_inputchanneldevicefailure_event) ba.raiseEventFromDifferentThread(Me, null, 0,event+"_inputchanneldevicefailure", false,null );
	}
	
	private void raise_inputchannelbuffering_event(String msg) {
		if (need_inputchannelbuffering_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_inputchannelbuffering", false, new Object[] {msg});
	}
	
	private void raise_inputchannelpcmdata_event(int length, byte[] bb) {
		if (need_inputchannelpcmdata_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_inputchannelpcmdata", false, new Object[] {length, bb});
	}
	
	private void raise_outputchanneldevicefailure_event(){
		if (need_outputchanneldevicefailure_event) ba.raiseEventFromDifferentThread(Me, null, 0,event+"_outputchanneldevicefailure", false,null );
	}
	
	private void raise_outputchannelbuffering_event(String msg) {
		if (need_outputchannelbuffering_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_outputchannelbuffering", false, new Object[] {msg});
	}
	
	private void raise_outputchannelpcmdata_event(int length, byte[] bb) {
		if (need_outputchannelpcmdata_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_outputchannelpcmdata", false, new Object[] {length, bb});
	}
	
	private void raise_soundchannelclosed_event() {
		if (need_soundchannelclosed_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_soundchannelclosed", false, null);
	}
	
	private void raise_soundchannelopened_event() {
		if (need_soundchannelopened_event) ba.raiseEventFromDifferentThread(Me, null, 0,event+"_soundchannelopened", false, null);
	}
	
	private void raise_inputchannellevel_event(int value) {
		if (need_inputchannellevel_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_inputchannellevel", false, new Object[] {value});
	}
	
	private void raise_outputchannellevel_event(int value) {
		if (need_outputchannellevel_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_outputchannellevel", false, new Object[] {value});
	}
	
	/**
	 * Dipanggil saat ShutdownHook
	 */
	private void save_config() {
		if (cc!=null) {
			try {
				gs.toJson(cc, new FileWriter(new File(runningfolder,"Config.json")));
				raise_log_event("Config.json saved");
			} catch (JsonIOException | IOException e) {
				raise_log_event("save_config failed, exception = "+e.getMessage());
			}
		}
	}
	
	/**
	 * Dipanggil saat initialization
	 */
	private void load_config() {
		try {
			CustomConfig xx = gs.fromJson(new FileReader(new File(runningfolder,"Config.json")), CustomConfig.class);
			raise_log_event("Config.json loaded");
			cc = xx;
		} catch (JsonSyntaxException | JsonIOException | FileNotFoundException e) {
			raise_log_event("load_config failed, exception = "+e.getMessage());
		}
		
	}
}
