using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Data;
using System.Windows.Input;
using KellermanSoftware.ContactsModule.Controllers;
using KellermanSoftware.ContactsModule.DataAccess;
using KellermanSoftware.ContactsModule.Events;
using KellermanSoftware.ContactsModule.Models;
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Unity;

namespace KellermanSoftware.ContactsModule.ViewModels
{
    public sealed class ContactsViewModel : IContactsViewModel
    {
        private readonly IUnityContainer _container;
        private readonly IRepository _repository;
        private readonly IEventAggregator _eventAggregator;
        private readonly IConfirmationController _confirmationController;
        private readonly ListCollectionView _contacts;
        private readonly List<ContactViewModel> _contactsList;
        private readonly DelegateCommand _editContactCommand;
        private readonly DelegateCommand _deleteContactCommand;
        
        public ContactsViewModel(IUnityContainer container,
                                 IRepository repository, 
                                 IEventAggregator eventAggregator,
                                 IConfirmationController confirmationController)
        {
            if (container == null)
            {
                throw new ArgumentNullException("container");
            }
            if (repository == null)
            {
                throw new ArgumentNullException("repository");
            }
            if (eventAggregator == null)
            {
                throw new ArgumentNullException("eventAggregator");
            }
            if (confirmationController == null)
            {
                throw new ArgumentNullException("confirmationController");
            }

            _container = container;
            _repository = repository;
            _eventAggregator = eventAggregator;
            _confirmationController = confirmationController;

            _contactsList = LoadContactsList();

            // Initialize the CollectionView and track the current selection.
            _contacts = new ListCollectionView(_contactsList);
            _contacts.CurrentChanged += OnCurrentContactChanged;

            AddContactCommand = new DelegateCommand(AddContact);
            _editContactCommand = new DelegateCommand(EditContact, 
                                                      CanEditContact);
            _deleteContactCommand = new DelegateCommand(OnDeleteCommand, 
                                                        CanDeleteContact);
        }

        #region IContactsViewModel Members

        public event EventHandler<ContactSelectedEventArgs> ContactSelected = delegate { };

        public event EventHandler EditContactDetailsRequested = delegate { };

        public void ReloadContactsList(long selectedContactId)
        {
            _contactsList.Clear();
            _contactsList.AddRange(LoadContactsList());
            _contacts.Refresh();

            foreach (ContactViewModel contact in _contacts)
            {
                if (contact.ContactId == selectedContactId)
                {
                    _contacts.MoveCurrentTo(contact);
                    break;
                }
            }
        }

        #endregion IContactsViewModel Members

        public ICollectionView Contacts 
        {
            get { return _contacts; }
        }

        public ICommand AddContactCommand { get; private set; }

        public ICommand EditContactCommand 
        {
            get { return _editContactCommand; }
        }

        public ICommand DeleteContactCommand
        {
            get { return _deleteContactCommand; }
        }

        private List<ContactViewModel> LoadContactsList()
        {
            return _repository.LoadContactNames().ConvertAll(x => new ContactViewModel(x));
        }

        private void OnCurrentContactChanged(object sender, EventArgs e)
        {
            ContactViewModel contactViewModel =
                (ContactViewModel)_contacts.CurrentItem as ContactViewModel;
            long? contactId = contactViewModel == null 
                                  ? (long?)null 
                                  : contactViewModel.ContactId;
            ContactSelected(this,
                            new ContactSelectedEventArgs(contactId));

            _editContactCommand.RaiseCanExecuteChanged();
            _deleteContactCommand.RaiseCanExecuteChanged();
        }

        private void AddContact()
        {
            // Create EditContactDetailsViewModel.
            ParameterOverride constructorParameter =
                new ParameterOverride("contact", new Contact());
            EditContactDetailsViewModel viewModel =
                _container.Resolve<EditContactDetailsViewModel>(constructorParameter);
            
            // Publish the EditContactDetailsEvent event.
            _eventAggregator.GetEvent<EditContactDetailsEvent>().Publish(viewModel);
        }

        private void EditContact()
        {
            EditContactDetailsRequested(this, EventArgs.Empty);
        }

        private bool CanEditContact()
        {
            return _contacts.CurrentItem != null;
        }

        private void OnDeleteCommand()
        {
            Debug.Assert(_contacts.CurrentItem != null);
            ContactViewModel contactViewModel = 
                _contacts.CurrentItem as ContactViewModel;
            if (contactViewModel == null)
            {
                Debug.Assert(false);
                return;
            }
            string message = 
                string.Format("Are you sure you want to delete {0} contact?",
                              contactViewModel.ContactName);
            _confirmationController.Show("Delete confirmation",
                                         message,
                                         result =>
                                         {
                                             if (result)
                                             {
                                                 DeleteContact();
                                             }
                                         });
        }

        private void DeleteContact()
        {
            Debug.Assert(_contacts.CurrentItem != null);
            ContactViewModel contactViewModel =
                _contacts.CurrentItem as ContactViewModel;
            if (contactViewModel == null)
            {
                Debug.Assert(false);
                return;
            }

            _repository.DeleteContact(contactViewModel.ContactId);

            _contacts.Remove(_contacts.CurrentItem);
            _editContactCommand.RaiseCanExecuteChanged();
            _deleteContactCommand.RaiseCanExecuteChanged();
        }

        private bool CanDeleteContact()
        {
            return _contacts.CurrentItem != null;
        }
    }
}
