// DO NOT EDIT! This is an autogenerated file. All changes will be
// overwritten!

//	Copyright (c) 2023 Vidyo, Inc. All rights reserved.


using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;

namespace VidyoClient
{
	/// <summary>
	/// Establishes and manages a point-to-point call.
	/// </summary>
	public class Call: IDisposable {
		private bool disposed = false; 
#if __IOS__
		const string importLib = "__Internal";
#else
		const string importLib = "libVidyoClient";
#endif
		private IntPtr objPtr; // opaque VidyoCall reference.
		private GCHandle objHandle; // weak VidyoCall reference.
		private List<WeakReference> objects = new List<WeakReference>();

		private void DisposeWeakReferenceObjects() {
			foreach(var item in objects) {
				if (item == null || !item.IsAlive || item.Target == null) { continue; }
				switch (item.Target.GetType().Name) {
					case "Participant": ((Participant)item.Target).Dispose(false); break;
					case "User": ((User)item.Target).Dispose(false); break;
				}
			}
			objects.Clear();
		}
		public IntPtr GetObjectPtr() {
			return objPtr;
		}
		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		[return: MarshalAs(UnmanagedType.I1)]
		private static extern Boolean VidyoCallAcceptNative(IntPtr c, OnInitiated onInitiated, OnEnded onEnded);

		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		private static extern IntPtr VidyoCallConstructCopyNative(IntPtr other);

		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		private static extern void VidyoCallDestructNative(IntPtr obj);

		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		private static extern IntPtr VidyoCallGetIdNative(IntPtr c);

		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		private static extern IntPtr VidyoCallGetSubjectNative(IntPtr c);

		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		private static extern IntPtr VidyoCallGetUserNative(IntPtr c);

		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		private static extern void VidyoCallHangUpNative(IntPtr c);

		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		[return: MarshalAs(UnmanagedType.I1)]
		private static extern Boolean VidyoCallInitiateNative(IntPtr c, OnInitiated onInitiated, OnEnded onEnded);

		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		[return: MarshalAs(UnmanagedType.I1)]
		private static extern Boolean VidyoCallRegisterMessageEventListenerNative(IntPtr c, OnMessageReceived onMessageReceived, OnMessageAcknowledged onMessageAcknowledged, OnMessageRead onMessageRead, OnMessageTypingIndication onMessageTypingIndication);

		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		[return: MarshalAs(UnmanagedType.I1)]
		private static extern Boolean VidyoCallRegisterSubjectEventListenerNative(IntPtr c, OnSetSubject onSetSubject);

		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		private static extern void VidyoCallRejectNative(IntPtr c);

		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		[return: MarshalAs(UnmanagedType.I1)]
		private static extern Boolean VidyoCallSendMessageNative(IntPtr c, IntPtr message);

		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		[return: MarshalAs(UnmanagedType.I1)]
		private static extern Boolean VidyoCallSetSubjectNative(IntPtr c, IntPtr subject);

		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr VidyoCallGetUserDataNative(IntPtr obj);

		[DllImport(importLib, CallingConvention = CallingConvention.Cdecl)]
		public static extern void VidyoCallSetUserDataNative(IntPtr obj, IntPtr userData);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate void OnEnded(IntPtr c, IntPtr p, CallEndReason reason);
		private OnEnded _mOnEnded = OnEndedDelegate;
		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate void OnInitiated(IntPtr c, CallInitiatedResult result, IntPtr p);
		private OnInitiated _mOnInitiated = OnInitiatedDelegate;
		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate void OnMessageAcknowledged(IntPtr c, IntPtr chatMessage);
		private OnMessageAcknowledged _mOnMessageAcknowledged = OnMessageAcknowledgedDelegate;
		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate void OnMessageRead(IntPtr c, IntPtr participant, long messageId);
		private OnMessageRead _mOnMessageRead = OnMessageReadDelegate;
		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate void OnMessageReceived(IntPtr c, IntPtr participant, IntPtr chatMessage);
		private OnMessageReceived _mOnMessageReceived = OnMessageReceivedDelegate;
		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate void OnMessageTypingIndication(IntPtr c, IntPtr participant, ChatMessage.ChatMessageTypingIndication typingIndication);
		private OnMessageTypingIndication _mOnMessageTypingIndication = OnMessageTypingIndicationDelegate;
		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate void OnSetSubject(IntPtr c, IntPtr subject);
		private OnSetSubject _mOnSetSubject = OnSetSubjectDelegate;
		/// <summary>
		/// Reasons why a VidyoCall is ending.
		/// </summary>
		public enum CallEndReason {
			/// <summary>Call was terminated locally.</summary>
			CallendreasonEnded,
			/// <summary>Local entity could not establish a signaling connection to its peer.</summary>
			CallendreasonConnectionFailed,
			/// <summary>Signaling connection was lost between the local entity and its peer.</summary>
			CallendreasonConnectionLost,
			/// <summary>Remote peer is busy and cannot accept the call.</summary>
			CallendreasonParticipantBusy,
			/// <summary>Remote peer rejected the call.</summary>
			CallendreasonParticipantRejected,
			/// <summary>Local entity had a call and the remote peer ended it.</summary>
			CallendreasonParticipantEnded,
			/// <summary>Miscellaneous error occurred.</summary>
			CallendreasonMiscError
		}
		/// <summary>
		/// Result of an attempt to enter a VidyoCall.
		/// </summary>
		public enum CallInitiatedResult {
			/// <summary>Call entered successfully.</summary>
			CallinitiatedresultOK,
			/// <summary>Enter request was cancelled.</summary>
			CallinitiatedresultCancelled,
			/// <summary>Server did not respond in a reasonable amount of time to the request to enter the room.</summary>
			CallinitiatedresultNoResponse,
			/// <summary>Call specified by the user does not exist.</summary>
			CallinitiatedresultUnknownParticipant,
			/// <summary>Server is rejecting the request to enter due to some miscellaneous problem with the request.</summary>
			CallinitiatedresultMiscLocalError,
			/// <summary>Server is rejecting the request to enter due to some miscellaneous problem of its own.</summary>
			CallinitiatedresultMiscRemoteError
		}
		public interface IAccept{

			void OnInitiated(CallInitiatedResult result, Participant p);
			void OnEnded(Participant p, CallEndReason reason);
		}
		public interface IRegisterMessageEventListener{

			void OnMessageReceived(Participant participant, ChatMessage chatMessage);
			void OnMessageAcknowledged(ChatMessage chatMessage);
			void OnMessageRead(Participant participant, long messageId);
			void OnMessageTypingIndication(Participant participant, ChatMessage.ChatMessageTypingIndication typingIndication);
		}
		public interface IRegisterSubjectEventListener{

			void OnSetSubject(String subject);
		}
		private IAccept _mIAccept;
		private IRegisterMessageEventListener _mIRegisterMessageEventListener;
		private IRegisterSubjectEventListener _mIRegisterSubjectEventListener;
		public Call(IntPtr other){
			objPtr = VidyoCallConstructCopyNative(other);
			if (objPtr != IntPtr.Zero) {
				objHandle = GCHandle.Alloc(this, GCHandleType.Weak);
				VidyoCallSetUserDataNative(objPtr, GCHandle.ToIntPtr(objHandle));
			}
		}
		~Call(){
			Dispose(false);
		}
		public void Dispose(){
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		public void Dispose(bool disposing){
			if(disposed) return;

			disposed = true;
			DisposeWeakReferenceObjects();
			if(objPtr != IntPtr.Zero) {
				VidyoCallSetUserDataNative(objPtr, IntPtr.Zero);
				VidyoCallDestructNative(objPtr);
				objPtr = IntPtr.Zero;
			}

			if(objHandle.IsAllocated) objHandle.Free();
		}

		/// <summary>
		/// Accepts an incoming VidyoCall that was notified by the VidyoUserOnCallCreatedByInvite callback.
		/// </summary>
		/// <param name="c">Pointer to the VidyoCall object.</param>
		/// <param name="onInitiated">Callback that is triggered when the enter operation completes. This could be a successful enter or a failure due to permissions, etc.</param>
		/// <param name="onEnded">Callback that is triggered when the room has been exited.</param>
		public Boolean Accept(IAccept _iIAccept) {
			if (objPtr == IntPtr.Zero) { return default; }
			_mIAccept = _iIAccept;

			Boolean ret = VidyoCallAcceptNative(objPtr, _mOnInitiated, _mOnEnded);

			return ret;
		}
		/// <summary>
		/// Gets the unique ID of the VidyoCall
		/// </summary>
		/// <param name="c">Pointer to the VidyoCall object.</param>
		public String GetId() {
			if (objPtr == IntPtr.Zero) { return default; }

			IntPtr ret = VidyoCallGetIdNative(objPtr);

			return (string)MarshalPtrToUtf8.GetInstance().MarshalNativeToManaged(ret);
		}
		/// <summary>
		/// Gets the current subject of the VidyoCall.
		/// </summary>
		/// <param name="c">Pointer to the VidyoCall object.</param>
		public String GetSubject() {
			if (objPtr == IntPtr.Zero) { return default; }

			IntPtr ret = VidyoCallGetSubjectNative(objPtr);

			return (string)MarshalPtrToUtf8.GetInstance().MarshalNativeToManaged(ret);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="c"></param>
		public User GetUser() {
			if (objPtr == IntPtr.Zero) { return default; }

			IntPtr ret = VidyoCallGetUserNative(objPtr);
			User csRet = null;
			if(ret != IntPtr.Zero) {
				var csRetPtr = User.VidyoUserGetUserDataNative(ret);
				csRet = csRetPtr == IntPtr.Zero ? new User(ret) : (User)GCHandle.FromIntPtr(csRetPtr).Target;
			}


			return csRet;
		}
		/// <summary>
		/// Hangs up an in-progress VidyoCall.
		/// </summary>
		/// <param name="c">Pointer to the VidyoCall object.</param>
		public void HangUp() {
			if (objPtr == IntPtr.Zero) { return; }

			VidyoCallHangUpNative(objPtr);
		}
		/// <summary>
		/// Initiates a VidyoCall.
		/// </summary>
		/// <param name="c">Pointer to the VidyoCall object.</param>
		/// <param name="onInitiated">Callback that is triggered when the enter operation completes. This could be a successful enter or a failure due to permissions, etc.</param>
		/// <param name="onEnded">Callback that is triggered when the room has been exited.</param>
		public Boolean Initiate(IAccept _iIAccept) {
			if (objPtr == IntPtr.Zero) { return default; }
			_mIAccept = _iIAccept;

			Boolean ret = VidyoCallInitiateNative(objPtr, _mOnInitiated, _mOnEnded);

			return ret;
		}
		/// <summary>
		/// Registers to get notified about message events.
		/// </summary>
		/// <param name="c">The VidyoCall object.</param>
		/// <param name="onMessageReceived">Callback that is triggered when a new message has been received.</param>
		/// <param name="onMessageAcknowledged">Callback that is triggered when a sent message has been acknowledged.</param>
		/// <param name="onMessageRead">Callback that is triggered when a sent message has been read.</param>
		/// <param name="onMessageTypingIndication">Callback that is triggered when a remote participant is typing.</param>
		public Boolean RegisterMessageEventListener(IRegisterMessageEventListener _iIRegisterMessageEventListener) {
			if (objPtr == IntPtr.Zero) { return default; }
			_mIRegisterMessageEventListener = _iIRegisterMessageEventListener;

			Boolean ret = VidyoCallRegisterMessageEventListenerNative(objPtr, _mOnMessageReceived, _mOnMessageAcknowledged, _mOnMessageRead, _mOnMessageTypingIndication);

			return ret;
		}
		/// <summary>
		/// Registers to get notified about subject events.
		/// </summary>
		/// <param name="c">The VidyoCall object.</param>
		/// <param name="onSetSubject">Callback that is triggered when the subject has been changed.</param>
		public Boolean RegisterSubjectEventListener(IRegisterSubjectEventListener _iIRegisterSubjectEventListener) {
			if (objPtr == IntPtr.Zero) { return default; }
			_mIRegisterSubjectEventListener = _iIRegisterSubjectEventListener;

			Boolean ret = VidyoCallRegisterSubjectEventListenerNative(objPtr, _mOnSetSubject);

			return ret;
		}
		/// <summary>
		/// Rejects an incoming VidyoCall that was notified by the VidyoUserFeedbackCallCreatedByInvite callback.
		/// </summary>
		/// <param name="c">Pointer to the VidyoCall object.</param>
		public void Reject() {
			if (objPtr == IntPtr.Zero) { return; }

			VidyoCallRejectNative(objPtr);
		}
		/// <summary>
		/// Sends a message to the remote user in the VidyoCall.
		/// </summary>
		/// <param name="c">Pointer to the VidyoCall object.</param>
		/// <param name="message">Message to be sent.</param>
		public Boolean SendMessage(String message) {
			if (objPtr == IntPtr.Zero) { return default; }

			IntPtr nMessage = MarshalPtrToUtf8.GetInstance().MarshalManagedToNative(message ?? string.Empty);
			Boolean ret = VidyoCallSendMessageNative(objPtr, nMessage);
			Marshal.FreeHGlobal(nMessage);

			return ret;
		}
		/// <summary>
		/// Sets a new subject on the VidyoCall.
		/// </summary>
		/// <param name="c">Pointer to the VidyoCall object.</param>
		/// <param name="subject">New subject of the VidyoCall.</param>
		public Boolean SetSubject(String subject) {
			if (objPtr == IntPtr.Zero) { return default; }

			IntPtr nSubject = MarshalPtrToUtf8.GetInstance().MarshalManagedToNative(subject ?? string.Empty);
			Boolean ret = VidyoCallSetSubjectNative(objPtr, nSubject);
			Marshal.FreeHGlobal(nSubject);

			return ret;
		}
#if __IOS__
[ObjCRuntime.MonoPInvokeCallback(typeof(OnEnded))]
#endif
		private static void OnEndedDelegate(IntPtr c, IntPtr p, CallEndReason reason){
			var csCPtr = c != IntPtr.Zero ? VidyoCallGetUserDataNative(c) : IntPtr.Zero;
			if(csCPtr == IntPtr.Zero) return;
			var csC = (Call)GCHandle.FromIntPtr(csCPtr).Target;

			Participant csP = null;
			if(p != IntPtr.Zero) {
				var csPPtr = Participant.VidyoParticipantGetUserDataNative(p);
				if (csPPtr == IntPtr.Zero) {
					csP = new Participant(p);
					if (csP != null) {
						csC?.objects?.Add(new WeakReference(csP));
					}
				} else {
					csP = (Participant)GCHandle.FromIntPtr(csPPtr).Target;
				}
			}

			csC?._mIAccept?.OnEnded(csP, reason);
			csC?.objects?.RemoveAll(item => (!item.IsAlive || item.Target == null));
		}
#if __IOS__
[ObjCRuntime.MonoPInvokeCallback(typeof(OnInitiated))]
#endif
		private static void OnInitiatedDelegate(IntPtr c, CallInitiatedResult result, IntPtr p){
			var csCPtr = c != IntPtr.Zero ? VidyoCallGetUserDataNative(c) : IntPtr.Zero;
			if(csCPtr == IntPtr.Zero) return;
			var csC = (Call)GCHandle.FromIntPtr(csCPtr).Target;

			Participant csP = null;
			if(p != IntPtr.Zero) {
				var csPPtr = Participant.VidyoParticipantGetUserDataNative(p);
				if (csPPtr == IntPtr.Zero) {
					csP = new Participant(p);
					if (csP != null) {
						csC?.objects?.Add(new WeakReference(csP));
					}
				} else {
					csP = (Participant)GCHandle.FromIntPtr(csPPtr).Target;
				}
			}

			csC?._mIAccept?.OnInitiated(result, csP);
		}
#if __IOS__
[ObjCRuntime.MonoPInvokeCallback(typeof(OnMessageAcknowledged))]
#endif
		private static void OnMessageAcknowledgedDelegate(IntPtr c, IntPtr chatMessage){
			var csCPtr = c != IntPtr.Zero ? VidyoCallGetUserDataNative(c) : IntPtr.Zero;
			if(csCPtr == IntPtr.Zero) return;
			var csC = (Call)GCHandle.FromIntPtr(csCPtr).Target;

			ChatMessage csChatMessage = new ChatMessage(chatMessage);
			csC?._mIRegisterMessageEventListener?.OnMessageAcknowledged(csChatMessage);
		}
#if __IOS__
[ObjCRuntime.MonoPInvokeCallback(typeof(OnMessageRead))]
#endif
		private static void OnMessageReadDelegate(IntPtr c, IntPtr participant, long messageId){
			var csCPtr = c != IntPtr.Zero ? VidyoCallGetUserDataNative(c) : IntPtr.Zero;
			if(csCPtr == IntPtr.Zero) return;
			var csC = (Call)GCHandle.FromIntPtr(csCPtr).Target;

			Participant csParticipant = null;
			if(participant != IntPtr.Zero) {
				var csParticipantPtr = Participant.VidyoParticipantGetUserDataNative(participant);
				if (csParticipantPtr == IntPtr.Zero) {
					csParticipant = new Participant(participant);
					if (csParticipant != null) {
						csC?.objects?.Add(new WeakReference(csParticipant));
					}
				} else {
					csParticipant = (Participant)GCHandle.FromIntPtr(csParticipantPtr).Target;
				}
			}

			csC?._mIRegisterMessageEventListener?.OnMessageRead(csParticipant, messageId);
		}
#if __IOS__
[ObjCRuntime.MonoPInvokeCallback(typeof(OnMessageReceived))]
#endif
		private static void OnMessageReceivedDelegate(IntPtr c, IntPtr participant, IntPtr chatMessage){
			var csCPtr = c != IntPtr.Zero ? VidyoCallGetUserDataNative(c) : IntPtr.Zero;
			if(csCPtr == IntPtr.Zero) return;
			var csC = (Call)GCHandle.FromIntPtr(csCPtr).Target;

			Participant csParticipant = null;
			if(participant != IntPtr.Zero) {
				var csParticipantPtr = Participant.VidyoParticipantGetUserDataNative(participant);
				if (csParticipantPtr == IntPtr.Zero) {
					csParticipant = new Participant(participant);
					if (csParticipant != null) {
						csC?.objects?.Add(new WeakReference(csParticipant));
					}
				} else {
					csParticipant = (Participant)GCHandle.FromIntPtr(csParticipantPtr).Target;
				}
			}

			ChatMessage csChatMessage = new ChatMessage(chatMessage);
			csC?._mIRegisterMessageEventListener?.OnMessageReceived(csParticipant, csChatMessage);
		}
#if __IOS__
[ObjCRuntime.MonoPInvokeCallback(typeof(OnMessageTypingIndication))]
#endif
		private static void OnMessageTypingIndicationDelegate(IntPtr c, IntPtr participant, ChatMessage.ChatMessageTypingIndication typingIndication){
			var csCPtr = c != IntPtr.Zero ? VidyoCallGetUserDataNative(c) : IntPtr.Zero;
			if(csCPtr == IntPtr.Zero) return;
			var csC = (Call)GCHandle.FromIntPtr(csCPtr).Target;

			Participant csParticipant = null;
			if(participant != IntPtr.Zero) {
				var csParticipantPtr = Participant.VidyoParticipantGetUserDataNative(participant);
				if (csParticipantPtr == IntPtr.Zero) {
					csParticipant = new Participant(participant);
					if (csParticipant != null) {
						csC?.objects?.Add(new WeakReference(csParticipant));
					}
				} else {
					csParticipant = (Participant)GCHandle.FromIntPtr(csParticipantPtr).Target;
				}
			}

			csC?._mIRegisterMessageEventListener?.OnMessageTypingIndication(csParticipant, typingIndication);
		}
#if __IOS__
[ObjCRuntime.MonoPInvokeCallback(typeof(OnSetSubject))]
#endif
		private static void OnSetSubjectDelegate(IntPtr c, IntPtr subject){
			var csCPtr = c != IntPtr.Zero ? VidyoCallGetUserDataNative(c) : IntPtr.Zero;
			if(csCPtr == IntPtr.Zero) return;
			var csC = (Call)GCHandle.FromIntPtr(csCPtr).Target;

			csC?._mIRegisterSubjectEventListener?.OnSetSubject((string)MarshalPtrToUtf8.GetInstance().MarshalNativeToManaged(subject));
		}
	};
}
