/******************************************************************************/
/*                                                                            */
/* Copyright Avaya LLC.                                                       */
/*                                                                            */
/******************************************************************************/
using System;
using System.Collections.Generic;
using Avaya.ClientServices;
using Avaya.ClientServices.Media;
using System.Media;

namespace CommunicationSampleApp.Classes
{
    class CallManager
    {
        public static CallManager CallManagerInstance;
        public static CallService CallServiceInstance;
        public static String ConferenceLockStatus = "Lock";
        public static Call OutgoingCall;
        public Call IncomingCall;
        public static Call CurrentCall;
        //public VideoChannel VideoChannelInstance;
        public static int ChannelID = 0;
        public static List<ActiveParticipant> ParticipantsList = new List<ActiveParticipant>();
        public static Conference ConferenceInstance = null;
        public List<VideoRenderer2> localVideo = new List<VideoRenderer2>();
        public List<VideoRenderer2> remoteVideo = new List<VideoRenderer2>();
        public List<VideoChannel> currentVideoChannels = new List<VideoChannel>();
        public CameraVideoSource cameraVideoSource;
        public CameraDevice selectedCamera;

        public event EventHandler IncomingCallEvent;
        public event EventHandler ParticipantsChangedEvent;
        public event EventHandler ConferenceStartedEvent;
        public event EventHandler ConferenceCapabilitiesChangedEvent;
        public event EventHandler CallStateChangedEvent;
        public event EventHandler MuteEvent;
        public event EventHandler CallStartedEvent;
        public event EventHandler CallEstablishedEvent;
        public event EventHandler CallIgnoredEvent;
        public event EventHandler CallEndedEvent;
        public event EventHandler UpdateVideoUIEvent;
        public event EventHandler RemoteAddressChangedEvent;

        private SoundPlayer soundPlayer = new SoundPlayer(CommunicationSampleApp.Properties.Resources.ringback);
        public static CallManager GetInstance()
        {
            if (CallManagerInstance == null)
            {
                CallManagerInstance = new CallManager();
            }
            return CallManagerInstance;
        }

        public void AddCallServiceEvents()
        {
            CallServiceInstance.IncomingCallReceived += IncomingCallReceived;
            CallServiceInstance.CallCreated += CallCreated;
        }

        public void RemoveCallServiceEvents()
        {
            CallServiceInstance.IncomingCallReceived -= IncomingCallReceived;
            CallServiceInstance.CallCreated -= CallCreated;
        }

        public void IncomingCallReceived(object sender, CallEventArgs e)
        {
            IncomingCall = e.Call;
            AddCall(IncomingCall);
            IncomingCallEvent(this, EventArgs.Empty);
        }
        public static void CallCreated(object sender, CallEventArgs e)
        {
        }

        /// <summary>
        /// Creates call object and added events handler to call object
        /// </summary>
        public void CreateAudioCall(String number)
        {
            if (CallServiceInstance != null)
            {
                OutgoingCall = CallServiceInstance.CreateCall();
                AddCall(OutgoingCall);
                OutgoingCall.RemoteAddress = number;
                CurrentCall = OutgoingCall;
            }
            else
            {
                NotificationHelper.ShowNotification("Please relogin call service");
            }
        }
        
        /// <summary>
        /// Starts the call with provided call object
        /// </summary>
        public void StartCall(Call call)
        {
            if (call != null)
            {
                call.Start();
            }

        }
        /// <summary>
        /// End the call session and remove the call object
        /// </summary>
        public void EndCall(Call call)
        {
            if (call != null)
            {
                call.End();
                RemoveCall(call);
            }
        }
        /// <summary>
        /// Ignores incomming call
        /// </summary>
        public void IgnoreIncomingCall()
        {
            if (IncomingCall != null)
            {
                IncomingCall.Ignore();
                RemoveCall(IncomingCall);
                if (CallIgnoredEvent != null)
                {
                    CallIgnoredEvent(this, EventArgs.Empty);
                }
            }
        }
        /// <summary>
        /// Accept incomming call as audio
        /// </summary>
        public void AcceptCall()
        {
            if (IncomingCall != null)
            {
                if (IncomingCall.IncomingVideoStatus == IncomingVideoStatus.Supported)
                {
                    AddVideo(IncomingCall);
                }
                IncomingCall.Accept();
                CurrentCall = IncomingCall;
            }
            IncomingCall = null;
        }
        /// <summary>
        /// Mute/Unmute audio of active call
        /// </summary>
        public void MuteCall()
        {
            if (CallServiceInstance != null)
            {
                Call call = CallServiceInstance.ActiveCall;
                if (call != null)
                {
                    if (call.AudioMuted)
                    {
                        call.MuteAudio(false, (error) => 
                        {
                            MuteEvent(this, EventArgs.Empty);
                        });
                    }
                    else
                    {
                        call.MuteAudio(true, (error) =>
                        {
                            MuteEvent(this, EventArgs.Empty);
                        });
                    }
                }
            }
            else
            {
                NotificationHelper.ShowNotification("Please relogin call service");
            }
        }

        /// <summary>
        /// Add video to call object
        /// </summary>
        public void AddVideo(Call call)
        {
            if (UpdateVideoUIEvent != null)
            {
                UpdateVideoUIEvent(this, EventArgs.Empty);
            }
            call.SetVideoMode(VideoMode.SendReceive, (error) =>
            {
                if (error != null)
                {
                    Console.Write("Set video mode error : " + error.Message);
                }
                else
                {
                    Console.Write("Set video mode success");
                    StartLocalVideo();
                }
            });
        }

        /// <summary>
        /// Starts local video
        /// </summary>
        private void StartLocalVideo()
        {
            SetCameraDevice();
            cameraVideoSource.setPreviewVideoSink(localVideo[0]);
            cameraVideoSource.Start(selectedCamera, new VideoCaptureFormat(700, 400, 0, 30), (result) => 
            {
                if (result == CameraVideoSourceResult.Success)
                {
                    Console.WriteLine("CameraCaptureStart Success");
                }
                else
                {
                    Console.WriteLine("Error :" + result.ToString());
                }
            });
        }

        /// <summary>
        /// Stops local video
        /// </summary>
        public void StopLocalVideo()
        {
            if (cameraVideoSource != null)
            {
                cameraVideoSource.Stop();
                cameraVideoSource.setVideoSink(null);
                cameraVideoSource.setPreviewVideoSink(null);
            }
        }

        /// <summary>
        /// Stops video window
        /// </summary>
        public void StopVideoWindow()
        {
            if (cameraVideoSource != null)
            {
                cameraVideoSource.Stop();
                cameraVideoSource.setVideoSink(null);
                cameraVideoSource.setPreviewVideoSink(null);
            }
        }

        /// <summary>
        /// select available camera
        /// </summary>
        public void SetCameraDevice()
        {
            cameraVideoSource = new CameraVideoSource();
            if (cameraVideoSource.Cameras.Count > 0)
            {
                selectedCamera = cameraVideoSource.Cameras[0];
            }
        }

        /// <summary>
        /// Add local video renderer in localVideo list.
        /// </summary>
        /// <param name="localVideoRenderer"></param>
        public void AddLocalVideoRenderer(VideoRenderer2 localVideoRenderer)
        {
            localVideo.Add(localVideoRenderer);
        }

        /// <summary>
        /// Add remote video renderer in remoteVideo list.
        /// </summary>
        /// <param name="remoteVideoRenderer"></param>
        public void AddRemoteVideoRenderer(VideoRenderer2 remoteVideoRenderer)
        {
            remoteVideo.Add(remoteVideoRenderer);
        }

        /// <summary>
        /// Convert input to DTMFTone and send DTMF to call
        /// </summary>
        public void SendDTMF(Char tone)
        {
            DtmfTone dtmftone = new DtmfTone();
            switch (tone)
            {
                case '0':
                    dtmftone = DtmfTone.Zero;
                    break;
                case '1':
                    dtmftone = DtmfTone.One;
                    break;
                case '2':
                    dtmftone = DtmfTone.Two;
                    break;
                case '3':
                    dtmftone = DtmfTone.Three;
                    break;
                case '4':
                    dtmftone = DtmfTone.Four;
                    break;
                case '5':
                    dtmftone = DtmfTone.Five;
                    break;
                case '6':
                    dtmftone = DtmfTone.Six;
                    break;
                case '7':
                    dtmftone = DtmfTone.Seven;
                    break;
                case '8':
                    dtmftone = DtmfTone.Eight;
                    break;
                case '9':
                    dtmftone = DtmfTone.Nine;
                    break;
                case '*':
                    dtmftone = DtmfTone.Star;
                    break;
                case 'A':
                    dtmftone = DtmfTone.A;
                    break;
                case 'B':
                    dtmftone = DtmfTone.B;
                    break;
                case 'C':
                    dtmftone = DtmfTone.C;
                    break;
                case 'D':
                    dtmftone = DtmfTone.D;
                    break;
                case '#':
                    dtmftone = DtmfTone.Pound;
                    break;
                default:
                    Console.Write("Enter Valid Character");
                    break;

            }
            
            SDKManager.GetUser().CallService.ActiveCall.SendDtmf(dtmftone);
        }

        public void StartRinging()
        {
            soundPlayer.PlayLooping();
        }

        public void StopRinging()
        {
            soundPlayer.Stop();
        }

        /// <summary>
        /// Add event handlers to call object
        /// </summary>
        public void AddCall(Call call)
        {
            call.Started += CallStarted;
            call.RemoteAlerting += CallRemoteAlerting;
            call.Ignored += CallIgnored;
            call.Established += CallEstablished;
            call.RemoteAddressChanged += CallRemoteAddressChanged;
            call.Ended += CallEnded;
            call.CapabilitiesChanged += CallCapabilitiesChanged;
            call.Held += CallHeld;
            call.VideoChannelsUpdated += VideoChannelsUpdated;
            call.AllowedVideoDirectionChanged += AllowedVideoDirectionChanged;
            call.ConferenceStatusChanged += ConferenceStatusChanged;
            call.Conference.ParticipantsChanged += ParticipantsChanged;
            call.Conference.Started += ConferenceStarted;
            call.Conference.CapabilitiesChanged += ConferenceCapabilitiesChanged;
            call.Conference.EventConferenceStatusChanged += EventConferenceStatusChanged;
        }
        /// <summary>
        /// Removes event handlers from call object
        /// </summary>
        public void RemoveCall(Call call)
        {
            call.Started -= CallStarted;
            call.RemoteAlerting -= CallRemoteAlerting;
            call.Ignored -= CallIgnored;
            call.Established -= CallEstablished;
            call.RemoteAddressChanged -= CallRemoteAddressChanged;
            call.Ended -= CallEnded;
            call.CapabilitiesChanged -= CallCapabilitiesChanged;
            call.Held -= CallHeld;
            call.VideoChannelsUpdated -= VideoChannelsUpdated;
            call.AllowedVideoDirectionChanged -= AllowedVideoDirectionChanged;
            call.ConferenceStatusChanged -= ConferenceStatusChanged;
            call.Conference.ParticipantsChanged -= ParticipantsChanged;
            call.Conference.Started -= ConferenceStarted;
            call.Conference.CapabilitiesChanged -= ConferenceCapabilitiesChanged;
            call.Conference.EventConferenceStatusChanged -= EventConferenceStatusChanged;
        }

        #region Call related event handlers

        void CallStarted(object sender, EventArgs e)
        {
            if (CallStateChangedEvent != null)
            {
                CallStateChangedEvent(this, EventArgs.Empty);
            }
            if (CallStartedEvent != null)
            {
                CallStartedEvent(this, EventArgs.Empty);
            }
        }

        void CallRemoteAlerting(object sender, RemoteAlertingEventArgs e)
        {
            StartRinging();
            if (CallStateChangedEvent != null)
            {
                CallStateChangedEvent(this, EventArgs.Empty);
            }
        }

        void CallIgnored(object sender, EventArgs e)
        {
            if (CallStateChangedEvent != null)
            {
                CallStateChangedEvent(this, EventArgs.Empty);
            }
            if (CallIgnoredEvent != null)
            {
                CallIgnoredEvent(this, EventArgs.Empty);
            }
        }

        void CallEstablished(object sender, EventArgs e)
        {
            StopRinging();
            if (CallStateChangedEvent != null)
            {
                CallStateChangedEvent(this, EventArgs.Empty);
            }
            if (CallEstablishedEvent != null)
            {
                CallEstablishedEvent(this, EventArgs.Empty);
            }
            Call call = (Call)sender;
            if (call.IsConference)
            {
                call.ConferenceStatusChanged += new EventHandler<ConferenceStatusChangedEventArgs>(ConferenceStatusChanged);
                call.Conference.ParticipantsChanged += ParticipantsChanged;
                call.Conference.Started += ConferenceStarted;
                call.Conference.CapabilitiesChanged += ConferenceCapabilitiesChanged;
            }

        }

        public void VideoChannelsUpdated(object sender, VideoChannelsEventArgs e)
        {
            if (e.VideoChannels != null)
            {
                if (e.VideoChannels.Count > 0 && e.VideoChannels[0].IsEnabled)
                {
                    VideoChannel videoChannel = e.VideoChannels[0];
                    StartVideoTransmission(videoChannel.ChannelId);
                    StartRemoteVideoRendering(videoChannel.ChannelId);
                }
            }
        }
        private void StartVideoTransmission(int channelId)
        {
            VideoSink sink = SDKManager.VideoInterfaceInstance.getLocalVideoSink(channelId);
            cameraVideoSource.setVideoSink(sink);
        }

        private void StopVideoTransmission()
        {
            if (cameraVideoSource != null)
            {
                cameraVideoSource.setVideoSink(null);
            }
        }
        private void StartRemoteVideoRendering(int channelId)
        {
            VideoRenderer2 video = remoteVideo[0];
            try
            {
                VideoSource videoSource = SDKManager.VideoInterfaceInstance.getRemoteVideoSource(channelId);
                videoSource.setVideoSink(video);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Remote rendering failed : " + ex.Message);
            }
        }
        private void StopRemoteVideoRendering(int channelId)
        {
            if (channelId != -1)
            {
                VideoSource videoSource = SDKManager.VideoInterfaceInstance.getRemoteVideoSource(channelId);

                if (videoSource == null)
                {
                    Console.WriteLine(string.Format("StopRemoteVideoRendering(): Unable to locate video source for channel id = {0}", channelId));
                }
                else
                {
                    SDKManager.VideoInterfaceInstance.getRemoteVideoSource(channelId).setVideoSink(null);
                }
            }
        }
        void CallRemoteAddressChanged(object sender, RemoteAddressChangedEventArgs e)
        {
            if (RemoteAddressChangedEvent != null)
            {
                RemoteAddressChangedEvent(this, EventArgs.Empty);
            }
        }

        void CallEnded(object sender, CallEndedEventArgs e)
        {
            StopRinging();
            if (IncomingCall != null)
            {
                RemoveCall(IncomingCall);
            }
            if (CallStateChangedEvent != null)
            {
                CallStateChangedEvent(this, EventArgs.Empty);
            }
            if (CallEndedEvent != null)
            {
                CallEndedEvent(this , EventArgs.Empty);
            }
        }
        void CallCapabilitiesChanged(object sender, EventArgs e)
        {   
        }
        void CallHeld(object sender, EventArgs e)
        {
            if (CallStateChangedEvent != null)
            {
                CallStateChangedEvent(this, EventArgs.Empty);
            }
        }
        
        public void ConferenceStatusChanged(object sender, EventArgs e)
        {
        }

        void ConferenceStarted(object sender, EventArgs e)
        {
            if (ConferenceStartedEvent != null)
            {
                ConferenceStartedEvent(this, EventArgs.Empty);
            }
        }

        void ConferenceCapabilitiesChanged(object sender, EventArgs e)
        {
            ConferenceInstance = (Conference)sender;
            if (ConferenceCapabilitiesChangedEvent != null)
            {
                ConferenceCapabilitiesChangedEvent(this, EventArgs.Empty);
            }
        }

        void ParticipantsChanged(object sender, DataCollectionChangedEventArgs<ActiveParticipant> participants)
        {
            ConferenceInstance = (Conference)sender;
            if (ParticipantsChangedEvent != null)
            {
                ParticipantsChangedEvent(this, EventArgs.Empty);
            }
            ParticipantsList = participants.ChangedItems;
        }

        void AllowedVideoDirectionChanged(object sender, AllowedVideoDirectionChangedEventArgs e)
        {
            Console.WriteLine(string.Format("New allowed video direction: {0}", e.VideoDirection));
        }

        void EventConferenceStatusChanged(object sender, BooleanEventArg e)
        {
            Console.WriteLine(string.Format("Conference changed status to Event Conference: {0}", e.Value));
        }

        #endregion Call related event handlers

        #region Conference - moderator controls
        public void UnmuteAll()
        {
            ConferenceInstance.UnmuteAllParticipants((error) =>
            {
                if (error != null)
                {
                    NotificationHelper.ShowNotification("UnmuteAll error : " + error.Message.ToString());
                }
            });
        }

        public void MuteAll()
        {
            ConferenceInstance.MuteAllParticipants((error) => 
            {
                if (error != null)
                {
                    NotificationHelper.ShowNotification("MuteAll error : " + error.Message.ToString());
                }
            });
        }

        public void LockConference()
        {
            if (ConferenceInstance.IsLocked)
            {
                ConferenceInstance.SetLocked(false, (error) => 
                {
                    if (error != null)
                    {
                        NotificationHelper.ShowNotification("Unlock error : " + error.Message.ToString());
                    }
                });
             }
             else
             {
                ConferenceInstance.SetLocked(true, (error) =>
                {
                    if (error != null)
                    {
                        NotificationHelper.ShowNotification("Lock error : " + error.Message.ToString());
                    }
                });
             }
        }
        #endregion Conference - moderator control
    }
}