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

namespace CommunicationSampleApp.Classes
{
    class ContactManager
    {
        public static ContactManager ContactManagerInstance;
        public static ContactService ContactServiceInstance;
        public ContactRetrievalWatcher ContactRetrievalWatcherInstance;
        public static SearchContactsWatcher searchContactsWatcherRetrival;
        public static List<Contact> ContactList = new List<Contact>();
        public static List<Contact> ContactSearchList = new List<Contact>();
        public event EventHandler ContactServiceAvailableEvent;
        public event EventHandler ContactServicesCapabilitiesChangedEvent;
        public event EventHandler GetContactDoneEvent;
        public event EventHandler UpdateContactUIEvent;
        public event EventHandler SearchContactDoneEvent;
        public event EventHandler SearchContactFailedEvent;
        public static Contact SelectedContact;
        public ContactManager()
        {
        }
        public static ContactManager GetInstance()
        {
            if (ContactManagerInstance == null)
            {
                ContactManagerInstance = new ContactManager();
            }
            return ContactManagerInstance;
        }
        /// <summary>
        /// Retrieve contacts from all contacts
        /// </summary>
        public void GetContacts()
        {
            ContactServiceInstance.GetContacts(ContactRetrievalWatcherInstance, ContactSourceType.AllContacts);
        }
        /// <summary>
        /// Add event handler to ContactRetrivalWatcher object to handle GetContacts request   
        /// </summary>
        public void AddcontactRetrievalWatcherEvent()
        {
            ContactRetrievalWatcherInstance.DataRetrievalDone += ContactDataRetrievalDone;
            ContactRetrievalWatcherInstance.DataRetrievalError += ContactDataRetrievalError;
            ContactRetrievalWatcherInstance.DataRetrievalProgress += ContactDataRetrievalProgress;
        }
        /// <summary>
        /// Removes event handler from ContactRetrivalWatcher object
        /// </summary>
        public void RemovecontactRetrievalWatcherEvent()
        {
            ContactRetrievalWatcherInstance.DataRetrievalDone -= ContactDataRetrievalDone;
            ContactRetrievalWatcherInstance.DataRetrievalError -= ContactDataRetrievalError;
            ContactRetrievalWatcherInstance.DataRetrievalProgress -= ContactDataRetrievalProgress;
        }

        private void ContactDataRetrievalProgress(object sender, DataRetrievalEventArgs<Contact> e)
        { 
        }

        private void ContactDataRetrievalError(object sender, ContactErrorEventArgs e)
        {
            NotificationHelper.ShowNotification("Contact Retrieval Error");
        }

        private void ContactDataRetrievalDone(object sender, EventArgs e)
        {
            ContactList = ((DataRetrievalWatcher<Contact, ContactErrorEventArgs>)sender).GetSnapshot();
            StartContactPresence();
            if (GetContactDoneEvent != null)
            {
                GetContactDoneEvent(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Add event handlers for register contact service to user object 
        /// </summary>
        public void RegisterForContactServiceEvents()
        {
            ContactServiceInstance.ContactServiceAvailable += ContactServiceAvailable;
            ContactServiceInstance.ContactLoadingComplete += ContactLoadingComplete;
            ContactServiceInstance.ContactServicesCapabilitiesChanged += ContactServicesCapabilitiesChanged;
            
            if (ContactRetrievalWatcherInstance != null)
            {
                RemovecontactRetrievalWatcherEvent();
                ContactRetrievalWatcherInstance = null;
            }
            ContactRetrievalWatcherInstance = new ContactRetrievalWatcher();
            AddcontactRetrievalWatcherEvent();
        }

        /// <summary>
        /// Removes event handlers of contact service from user object 
        /// </summary>
        public void UnregisterFromContactServiceEvents()
        {
            ContactServiceInstance.ContactServiceAvailable -= ContactServiceAvailable;
            ContactServiceInstance.ContactLoadingComplete -= ContactLoadingComplete;
        }

        public void ContactServiceAvailable(object sender, EventArgs e)
        {
            if (ContactServiceAvailableEvent != null)
            {
                ContactServiceAvailableEvent(this,EventArgs.Empty);
            }
        }

        public void ContactServicesCapabilitiesChanged(object sender, EventArgs e)
        {
            if (ContactServicesCapabilitiesChangedEvent != null)
            {
                ContactServicesCapabilitiesChangedEvent(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Event handler for Contact loading complete in contact data retrieval 
        /// </summary>
        public static void ContactLoadingComplete(object sender, EventArgs e)
        {
        }

        /// <summary>
        /// Add new contact with given values
        /// </summary>
        /// <param name="firstname"></param>
        /// <param name="lastname"></param>
        /// <param name="number"></param>
        /// <param name="email"></param>
        public void AddContact(String firstname, String lastname, String number, String email)
        {
            if (!ContactServiceInstance.AddContactCapability.Allowed)
            {
                Console.Write("Add contact capability denied.");
                return;
            }
            EditableContact newEditableContact = ContactServiceInstance.CreateEditableContact();
            if (newEditableContact == null)
            {
                Console.Write("Contact can not be properly created. Operation rejected.");
                return;
            }
            if (newEditableContact.FirstName.WriteCapability.Allowed)
            {
                newEditableContact.FirstName.Value = firstname;
            }

            if (newEditableContact.LastName.WriteCapability.Allowed)
            {
                newEditableContact.LastName.Value = lastname;
            }

            if (newEditableContact.EmailAddresses.WriteCapability.Allowed)
            {
                EditableContactEmailAddressField emailItem = new EditableContactEmailAddressField { Address = email, Type = ContactEmailAddressType.Work, Label = ""};
                newEditableContact.EmailAddresses.Values.Add(emailItem);
            }

            // Optionally add phone number
            if (newEditableContact.PhoneNumbers.WriteCapability.Allowed)
            {
                EditableContactPhoneField phoneItem = new EditableContactPhoneField { IsDefault = true, Type = ContactPhoneNumberType.Work, PhoneNumber = number};
                newEditableContact.PhoneNumbers.Values.Add(phoneItem);
            }

            ContactServiceInstance.AddContact(newEditableContact, (addContactEventArgs) => 
            {
                if (addContactEventArgs.NewContact != null)
                {
                    UpdateContactUIEvent(this, EventArgs.Empty);
                }
                else
                {
                    NotificationHelper.ShowNotification("Add Contact failed :" + addContactEventArgs.Error.ToString());
                }
            });
        }

        /// <summary>
        /// Update contact with new values
        /// </summary>
        /// <param name="firstname"></param>
        /// <param name="lastname"></param>
        /// <param name="number"></param>
        /// <param name="email"></param>
        public void UpdateContact(String firstname, String lastname, String number, String email)
        {
            if (SelectedContact != null)
            {
                if (SelectedContact.UpdateContactCapability.Allowed)
                {
                    Console.Write("Sending request to update contact");

                    EditableContact editableContact = ContactServiceInstance.CreateEditableContactFromContact(SelectedContact);

                    if (editableContact != null)
                    {
                        if (editableContact.FirstName.WriteCapability.Allowed)
                        {
                            editableContact.FirstName.Value = firstname;
                        }

                        if (editableContact.LastName.WriteCapability.Allowed)
                        {
                            editableContact.LastName.Value = lastname;
                        }

                        //Check if work-email was updated in edit operation
                        if (editableContact.EmailAddresses.WriteCapability.Allowed)
                        {
                            foreach (ContactEmailAddressField emailaddress in editableContact.EmailAddresses.Values)
                            {
                                if (emailaddress.Type == ContactEmailAddressType.Work)
                                {
                                    if (!emailaddress.Address.Equals(email))
                                    {
                                        editableContact.EmailAddresses.Values.Remove((EditableContactEmailAddressField)emailaddress);
                                        EditableContactEmailAddressField emailItem = new EditableContactEmailAddressField();
                                        emailItem.Address = email;
                                        emailItem.Type = ContactEmailAddressType.Work;
                                        editableContact.EmailAddresses.Values.Add(emailItem);
                                        break;
                                    }
                                }
                            }
                        }
                        //Check if work-phone number was updated in edit operation
                        if (editableContact.PhoneNumbers.WriteCapability.Allowed)
                        {
                            foreach (EditableContactPhoneField phone in editableContact.PhoneNumbers.Values)
                            {
                                if (phone.Type == ContactPhoneNumberType.Work)
                                {
                                    if (!phone.PhoneNumber.Equals(number))
                                    {
                                        editableContact.PhoneNumbers.Values.Remove(phone);
                                        EditableContactPhoneField phoneItem = new EditableContactPhoneField();
                                        phoneItem.IsDefault = true;
                                        phoneItem.Type = ContactPhoneNumberType.Work;
                                        phoneItem.PhoneNumber = number;
                                        editableContact.PhoneNumbers.Values.Add(phoneItem);
                                        break;
                                    }
                                }
                            }
                        }
                        ContactServiceInstance.UpdateContact(editableContact, (updateContactEventArgs) => 
                        {
                            if (updateContactEventArgs.UpdatedContact != null)
                            {
                                UpdateContactUIEvent(this, EventArgs.Empty);
                            }
                            else
                            {
                                NotificationHelper.ShowNotification("Update contact failed :" + updateContactEventArgs.Error.ToString());
                            }
                        });
                    }
                    else
                    {
                        NotificationHelper.ShowNotification("Can't edit contact. Cannot create editable contact.");
                        UpdateContactUIEvent(this, EventArgs.Empty);
                    }
                }
                else
                {
                    Console.Write("Can't edit contact . Capability not allowed.");
                }
            }
            else
            {
                Console.Write("Invalid contact");
            }
        }

        /// <summary>
        /// Delete contact
        /// </summary>
        public void DeleteContact()
        {
            ContactManager.ContactServiceInstance.DeleteContact(SelectedContact, (contactErrorEventArgs) => 
            {
                UpdateContactUIEvent(this, EventArgs.Empty);
            });
        }

        /// <summary>
        /// Start presence subscribe for contacts
        /// </summary>
        public void StartContactPresence()
        {
            foreach (Contact contact in ContactManager.ContactList)
            {
                AccessControlBehavior accessCntrlBehavior = AccessControlBehavior.Prompt;
                contact.PresenceChanged += OnContactPresenceChanged;
                contact.StartPresence(accessCntrlBehavior, (error) => 
                {
                });
            }
        }

        void OnContactPresenceChanged(object sender, ContactPresenceChangedEventArgs e)
        {
            Contact contact = e.ContactInfo;
            // if contact is not buddy, then we assume that it was a presence snapshot and we should stop presence explicitly
            if (contact.IsBuddy.Value == false)
            {
                contact.StopPresence((arg) =>
                {
                    contact.PresenceChanged -= OnContactPresenceChanged;
                });
            }
            if (GetContactDoneEvent != null)
            {
                GetContactDoneEvent(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Stop presence subscribe for contacts
        /// </summary>
        public void StopContactPresence()
        {

            foreach (Contact contact in ContactManager.ContactList)
            {
                contact.PresenceChanged -= OnContactPresenceChanged;
                contact.StopPresence((error) => 
                {
                });
            }
        }

        /// <summary>
        /// Search contact with provided contact search info   
        /// </summary>
        /// <param name="contactinfo"></param>
        public void SearchContactWithSearchString(String contactinfo)
        {
            uint maxResults = 300;
            uint chunkSize = 50;
            SearchScopeType scopeType = SearchScopeType.All;
            SearchSourceType sourceType = SearchSourceType.All;
            if (searchContactsWatcherRetrival == null)
            {
                searchContactsWatcherRetrival = new SearchContactsWatcher();
            }
            AddSearchContactsWatcherRetrival();
            ContactServiceInstance.SearchContacts(searchContactsWatcherRetrival, contactinfo, scopeType, sourceType, maxResults, chunkSize);
        }

        public void StopSearchContact()
        {
            if (searchContactsWatcherRetrival != null)
            {
                searchContactsWatcherRetrival.Cancel();
            }
        }

        public void AddSearchContactsWatcherRetrival()
        {
            searchContactsWatcherRetrival.DataRetrievalCollectionChanged += WatcherDataRetrievalCollectionChanged;
            searchContactsWatcherRetrival.DataRetrievalDone += WatcherDataRetrievalDone;
            searchContactsWatcherRetrival.DataRetrievalError += WatcherDataRetrievalError;
            searchContactsWatcherRetrival.DataRetrievalProgress += WatcherDataRetrievalProgress;
        }

        public void RemoveSearchContactDataRetrieval()
        {
            searchContactsWatcherRetrival.DataRetrievalCollectionChanged -= WatcherDataRetrievalCollectionChanged;
            searchContactsWatcherRetrival.DataRetrievalDone -= WatcherDataRetrievalDone;
            searchContactsWatcherRetrival.DataRetrievalError -= WatcherDataRetrievalError;
            searchContactsWatcherRetrival.DataRetrievalProgress -= WatcherDataRetrievalProgress;
        }

        public void WatcherDataRetrievalCollectionChanged(object sender, EventArgs e)
        {
        }

        public void WatcherDataRetrievalDone(object sender, EventArgs e)
        {
            ContactSearchList = searchContactsWatcherRetrival.GetSnapshot();
            StartContactSearchPresence();
            SearchContactDoneEvent(this, EventArgs.Empty);
        }

        public void WatcherDataRetrievalError(object sender, DirectorySearchFailureEventArgs e)
        {
            NotificationHelper.ShowNotification("Contact search failed : " + e.Error.ToString());
            SearchContactFailedEvent(this, EventArgs.Empty);
            RemoveSearchContactDataRetrieval();
        }

        public void WatcherDataRetrievalProgress(object sender, Avaya.ClientServices.DataRetrievalEventArgs<Contact> e)
        {
        }

        /// <summary>
        ///Start presence subscribe for contacts in search result 
        /// </summary>
        public void StartContactSearchPresence()
        {
            foreach (Contact contact in ContactManager.ContactSearchList)
            {
                AccessControlBehavior accessCntrlBehavior = AccessControlBehavior.Prompt;
                contact.PresenceChanged += OnContactSearchPresenceChanged;
                contact.StartPresence(accessCntrlBehavior, (presenceFailureEventArgs) => 
                {
                });
            }
        }

        void OnContactSearchPresenceChanged(object sender, ContactPresenceChangedEventArgs e)
        {
            Contact contact = e.ContactInfo;
            // if contact is not buddy, then we assume that it was a presence snapshot and we should stop presence explicitly
            if (contact.IsBuddy.Value == false)
            {
                contact.StopPresence((arg) =>
                {
                    contact.PresenceChanged -= OnContactPresenceChanged;
                });
            }
        }

        /// <summary>
        ///Stop presence subscribe for contacts in search result
        /// </summary>
        public void StopContactSearchPresence()
        {
            if (ContactManager.ContactSearchList.Count > 0)
            {

                foreach (Contact contact in ContactManager.ContactSearchList)
                {
                    contact.PresenceChanged -= OnContactSearchPresenceChanged;
                    contact.StopPresence((presenceFailureEventArgs) =>
                    {
                    });
                }
            }
        }
    }
}