using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;

namespace XLAB
{
    internal sealed class TypeSizeDirectoryWindowViewModel : ObservableObject
    {
        private readonly ITypeSizeDialogService _dialogService;
        private readonly TypeSizeDirectoryService _service;
        private readonly Dictionary<int, List<TprzDirectoryItem>> _tprzSearchIndex;
        private List<TipsDirectoryItem> _tipsCache;
        private List<TprzDirectoryItem> _currentTprzCache;
        private bool _isBusy;
        private string _searchText;
        private TipsDirectoryItem _selectedTips;
        private TprmcpDirectoryItem _selectedTprmcp;
        private TprmkDirectoryItem _selectedTprmk;
        private TprzDirectoryItem _selectedTprz;
        private string _statusText;

        public TypeSizeDirectoryWindowViewModel(TypeSizeDirectoryService service, ITypeSizeDialogService dialogService)
        {
            _service = service;
            _dialogService = dialogService;
            _tipsCache = new List<TipsDirectoryItem>();
            _currentTprzCache = new List<TprzDirectoryItem>();
            _tprzSearchIndex = new Dictionary<int, List<TprzDirectoryItem>>();

            TipsItems = new ObservableCollection<TipsDirectoryItem>();
            TprzItems = new ObservableCollection<TprzDirectoryItem>();
            TprmcpItems = new ObservableCollection<TprmcpDirectoryItem>();
            TprmkItems = new ObservableCollection<TprmkDirectoryItem>();

            AddTipsCommand = new RelayCommand(delegate { AddTipsAsync(); }, delegate { return !IsBusy; });
            EditTipsCommand = new RelayCommand(delegate { EditTipsAsync(); }, delegate { return !IsBusy && SelectedTips != null; });
            DeleteTipsCommand = new RelayCommand(delegate { DeleteTipsAsync(); }, delegate { return !IsBusy && SelectedTips != null; });
            ManageTpvdklCommand = new RelayCommand(delegate { ManageTpvdkl(); }, delegate { return !IsBusy && SelectedTips != null; });
            AddTprzCommand = new RelayCommand(delegate { AddTprzAsync(); }, delegate { return !IsBusy && SelectedTips != null; });
            EditTprzCommand = new RelayCommand(delegate { EditTprzAsync(); }, delegate { return !IsBusy && SelectedTprz != null; });
            DeleteTprzCommand = new RelayCommand(delegate { DeleteTprzAsync(); }, delegate { return !IsBusy && SelectedTprz != null; });
            AddTprmcpCommand = new RelayCommand(delegate { AddTprmcpAsync(); }, delegate { return !IsBusy && SelectedTprz != null; });
            EditTprmcpCommand = new RelayCommand(delegate { EditTprmcpAsync(); }, delegate { return !IsBusy && SelectedTprmcp != null; });
            DeleteTprmcpCommand = new RelayCommand(delegate { DeleteTprmcpAsync(); }, delegate { return !IsBusy && SelectedTprmcp != null; });
            AddTprmkCommand = new RelayCommand(delegate { AddTprmkAsync(); }, delegate { return !IsBusy && SelectedTprz != null; });
            EditTprmkCommand = new RelayCommand(delegate { EditTprmkAsync(); }, delegate { return !IsBusy && SelectedTprmk != null; });
            DeleteTprmkCommand = new RelayCommand(delegate { DeleteTprmkAsync(); }, delegate { return !IsBusy && SelectedTprmk != null; });
            RefreshCommand = new RelayCommand(delegate { RefreshAsync(); }, delegate { return !IsBusy; });

            UpdateStatus();
        }

        public ObservableCollection<TipsDirectoryItem> TipsItems { get; private set; }
        public ObservableCollection<TprzDirectoryItem> TprzItems { get; private set; }
        public ObservableCollection<TprmcpDirectoryItem> TprmcpItems { get; private set; }
        public ObservableCollection<TprmkDirectoryItem> TprmkItems { get; private set; }
        public ICommand AddTipsCommand { get; private set; }
        public ICommand EditTipsCommand { get; private set; }
        public ICommand DeleteTipsCommand { get; private set; }
        public ICommand ManageTpvdklCommand { get; private set; }
        public ICommand AddTprzCommand { get; private set; }
        public ICommand EditTprzCommand { get; private set; }
        public ICommand DeleteTprzCommand { get; private set; }
        public ICommand AddTprmcpCommand { get; private set; }
        public ICommand EditTprmcpCommand { get; private set; }
        public ICommand DeleteTprmcpCommand { get; private set; }
        public ICommand AddTprmkCommand { get; private set; }
        public ICommand EditTprmkCommand { get; private set; }
        public ICommand DeleteTprmkCommand { get; private set; }
        public ICommand RefreshCommand { get; private set; }

        public bool IsBusy
        {
            get { return _isBusy; }
            private set
            {
                if (SetProperty(ref _isBusy, value))
                {
                    RaiseCommandStates();
                }
            }
        }

        public TipsDirectoryItem SelectedTips
        {
            get { return _selectedTips; }
            set
            {
                if (SetProperty(ref _selectedTips, value))
                {
                    RaiseCommandStates();
                    LoadTprzForSelection();
                    UpdateStatus();
                }
            }
        }

        public TprzDirectoryItem SelectedTprz
        {
            get { return _selectedTprz; }
            set
            {
                if (SetProperty(ref _selectedTprz, value))
                {
                    RaiseCommandStates();
                    LoadLeafTablesForSelection();
                    UpdateStatus();
                }
            }
        }

        public TprmcpDirectoryItem SelectedTprmcp
        {
            get { return _selectedTprmcp; }
            set
            {
                if (SetProperty(ref _selectedTprmcp, value))
                {
                    RaiseCommandStates();
                    UpdateStatus();
                }
            }
        }

        public TprmkDirectoryItem SelectedTprmk
        {
            get { return _selectedTprmk; }
            set
            {
                if (SetProperty(ref _selectedTprmk, value))
                {
                    RaiseCommandStates();
                    UpdateStatus();
                }
            }
        }

        public string StatusText
        {
            get { return _statusText; }
            private set { SetProperty(ref _statusText, value); }
        }

        public string SearchText
        {
            get { return _searchText; }
            set
            {
                if (SetProperty(ref _searchText, value))
                {
                    ApplySearchFilter();
                    UpdateStatus();
                }
            }
        }

        public async Task InitializeAsync()
        {
            await ExecuteBusyOperationAsync(async delegate { await RefreshTipsCoreAsync(null); });
        }

        private void AddTipsAsync()
        {
            var result = _dialogService.ShowTipsEditDialog(new TipsDirectoryItem(), true, _tipsCache.ToList(), _service);
            if (result == null) return;
            RunMutationOperation(async delegate
            {
                var createdId = await Task.Run(delegate { return _service.AddTipsItem(result); });
                await RefreshTipsCoreAsync(createdId);
                _dialogService.ShowInfo("Запись TIPS добавлена.");
            });
        }

        private void EditTipsAsync()
        {
            if (SelectedTips == null) return;
            var result = _dialogService.ShowTipsEditDialog(CloneTips(SelectedTips), false, _tipsCache.ToList(), _service);
            if (result == null) return;
            RunMutationOperation(async delegate
            {
                await Task.Run(delegate { _service.UpdateTipsItem(result); });
                await RefreshTipsCoreAsync(result.Id);
                _dialogService.ShowInfo("Запись TIPS обновлена.");
            });
        }

        private void DeleteTipsAsync()
        {
            if (SelectedTips == null) return;
            var selected = SelectedTips;
            if (!_dialogService.Confirm(string.Format("Удалить тип СИ \"{0}\"?", selected.TypeName))) return;
            RunMutationOperation(async delegate
            {
                var result = await Task.Run(delegate { return _service.DeleteTipsItem(selected.Id); });
                if (!result.IsDeleted) { _dialogService.ShowWarning(result.WarningMessage); return; }
                await RefreshTipsCoreAsync(null);
                _dialogService.ShowInfo("Запись TIPS удалена.");
            });
        }

        private void ManageTpvdkl()
        {
            if (SelectedTips != null)
            {
                _dialogService.ShowTpvdklManagerDialog(CloneTips(SelectedTips), _service);
            }
        }

        private void AddTprzAsync()
        {
            if (SelectedTips == null) return;
            var result = _dialogService.ShowTprzEditDialog(new TprzDirectoryItem { TipsId = SelectedTips.Id }, true, _currentTprzCache.ToList(), _service);
            if (result == null) return;
            RunMutationOperation(async delegate
            {
                var createdId = await Task.Run(delegate { return _service.AddTprzItem(result); });
                await RefreshTipsCoreAsync(result.TipsId);
                if (SelectedTips != null && SelectedTips.Id == result.TipsId && SelectedTprz != null && SelectedTprz.Id != createdId)
                {
                    await RefreshTprzCoreAsync(result.TipsId, createdId);
                }
                _dialogService.ShowInfo("Запись TPRZ добавлена.");
            });
        }

        private void EditTprzAsync()
        {
            if (SelectedTprz == null) return;
            var result = _dialogService.ShowTprzEditDialog(CloneTprz(SelectedTprz), false, _currentTprzCache.ToList(), _service);
            if (result == null) return;
            RunMutationOperation(async delegate
            {
                await Task.Run(delegate { _service.UpdateTprzItem(result); });
                await RefreshTipsCoreAsync(result.TipsId);
                if (SelectedTips != null && SelectedTips.Id == result.TipsId && SelectedTprz != null && SelectedTprz.Id != result.Id)
                {
                    await RefreshTprzCoreAsync(result.TipsId, result.Id);
                }
                _dialogService.ShowInfo("Запись TPRZ обновлена.");
            });
        }

        private void DeleteTprzAsync()
        {
            if (SelectedTprz == null) return;
            var selected = SelectedTprz;
            if (!_dialogService.Confirm(string.Format("Удалить типоразмер \"{0}\"?", selected.RangeText))) return;
            RunMutationOperation(async delegate
            {
                var result = await Task.Run(delegate { return _service.DeleteTprzItem(selected.Id); });
                if (!result.IsDeleted) { _dialogService.ShowWarning(result.WarningMessage); return; }
                await RefreshTipsCoreAsync(selected.TipsId);
                _dialogService.ShowInfo("Запись TPRZ удалена.");
            });
        }

        private void AddTprmcpAsync()
        {
            if (SelectedTprz == null) return;
            if (TprmkItems.Count == 0)
            {
                _dialogService.ShowWarning("Сначала заполните таблицу TPRMK для выбранного типоразмера, затем добавляйте цикл и период в TPRMCP.");
                return;
            }

            var result = _dialogService.ShowTprmcpEditDialog(new TprmcpDirectoryItem { TypeSizeId = SelectedTprz.Id }, true, TprmcpItems.ToList(), _service);
            if (result == null) return;
            RunMutationOperation(async delegate
            {
                var createdId = await Task.Run(delegate { return _service.AddTprmcpItem(result); });
                await RefreshLeafTablesCoreAsync(SelectedTprz.Id, createdId, null);
                _dialogService.ShowInfo("Запись TPRMCP добавлена.");
            });
        }

        private void EditTprmcpAsync()
        {
            if (SelectedTprmcp == null) return;
            var result = _dialogService.ShowTprmcpEditDialog(CloneTprmcp(SelectedTprmcp), false, TprmcpItems.ToList(), _service);
            if (result == null) return;
            RunMutationOperation(async delegate
            {
                await Task.Run(delegate { _service.UpdateTprmcpItem(result); });
                await RefreshLeafTablesCoreAsync(result.TypeSizeId, result.Id, null);
                _dialogService.ShowInfo("Запись TPRMCP обновлена.");
            });
        }

        private void DeleteTprmcpAsync()
        {
            if (SelectedTprmcp == null) return;
            var selected = SelectedTprmcp;
            if (!_dialogService.Confirm(string.Format("Удалить период МК \"{0}\" мес.?", selected.PeriodMonths))) return;
            RunMutationOperation(async delegate
            {
                var result = await Task.Run(delegate { return _service.DeleteTprmcpItem(selected.Id); });
                if (!result.IsDeleted) { _dialogService.ShowWarning(result.WarningMessage); return; }
                await RefreshLeafTablesCoreAsync(selected.TypeSizeId, null, null);
                _dialogService.ShowInfo("Запись TPRMCP удалена.");
            });
        }

        private void AddTprmkAsync()
        {
            if (SelectedTprz == null) return;
            var result = _dialogService.ShowTprmkEditDialog(new TprmkDirectoryItem { TypeSizeId = SelectedTprz.Id }, true, TprmkItems.ToList(), _service);
            if (result == null) return;
            RunMutationOperation(async delegate
            {
                var createdId = await Task.Run(delegate { return _service.AddTprmkItem(result); });
                await RefreshLeafTablesCoreAsync(SelectedTprz.Id, null, createdId);
                _dialogService.ShowInfo("Запись TPRMK добавлена.");
            });
        }

        private void EditTprmkAsync()
        {
            if (SelectedTprmk == null) return;
            var result = _dialogService.ShowTprmkEditDialog(CloneTprmk(SelectedTprmk), false, TprmkItems.ToList(), _service);
            if (result == null) return;
            RunMutationOperation(async delegate
            {
                await Task.Run(delegate { _service.UpdateTprmkItem(result); });
                await RefreshLeafTablesCoreAsync(result.TypeSizeId, null, result.Id);
                _dialogService.ShowInfo("Запись TPRMK обновлена.");
            });
        }

        private void DeleteTprmkAsync()
        {
            if (SelectedTprmk == null) return;
            var selected = SelectedTprmk;
            if (!_dialogService.Confirm(string.Format("Удалить регламент МК \"{0}\"?", selected.VerificationTypeName))) return;
            RunMutationOperation(async delegate
            {
                var result = await Task.Run(delegate { return _service.DeleteTprmkItem(selected.Id); });
                if (!result.IsDeleted) { _dialogService.ShowWarning(result.WarningMessage); return; }
                await RefreshLeafTablesCoreAsync(selected.TypeSizeId, null, null);
                _dialogService.ShowInfo("Запись TPRMK удалена.");
            });
        }

        private async Task ExecuteBusyOperationAsync(Func<Task> operation)
        {
            try { IsBusy = true; await operation(); }
            catch (Exception ex) { _dialogService.ShowError(ex.Message); }
            finally { IsBusy = false; }
        }

        private async Task ExecuteMutationOperationAsync(Func<Task> operation)
        {
            try { IsBusy = true; await operation(); }
            catch (InvalidOperationException ex) { _dialogService.ShowWarning(ex.Message); }
            catch (Exception ex) { _dialogService.ShowError(ex.Message); }
            finally { IsBusy = false; }
        }

        private async Task RefreshTipsCoreAsync(int? tipsIdToSelect)
        {
            var currentTipsId = SelectedTips == null ? (int?)null : SelectedTips.Id;
            var currentTprzId = SelectedTprz == null ? (int?)null : SelectedTprz.Id;
            var tipsTask = Task.Run(delegate { return _service.LoadTipsItems(); });
            var tprzSearchTask = Task.Run(delegate { return _service.LoadTprzSearchItems(); });
            await Task.WhenAll(tipsTask, tprzSearchTask);

            _tipsCache = tipsTask.Result.ToList();
            _tprzSearchIndex.Clear();
            foreach (var group in tprzSearchTask.Result.GroupBy(delegate(TprzDirectoryItem item) { return item.TipsId; }))
            {
                _tprzSearchIndex[group.Key] = group.ToList();
            }

            ApplyTipsFilter(tipsIdToSelect.HasValue ? tipsIdToSelect : currentTipsId);
            if (SelectedTips == null)
            {
                _currentTprzCache = new List<TprzDirectoryItem>();
                TprzItems.Clear();
                TprmcpItems.Clear();
                TprmkItems.Clear();
                SelectedTprz = null;
                SelectedTprmcp = null;
                SelectedTprmk = null;
                UpdateStatus();
                return;
            }

            var tprzIdToSelect = currentTipsId.HasValue && currentTipsId.Value == SelectedTips.Id ? currentTprzId : null;
            await RefreshTprzCoreAsync(SelectedTips.Id, tprzIdToSelect);
            UpdateStatus();
        }

        private async Task RefreshTprzCoreAsync(int tipsId, int? tprzIdToSelect)
        {
            var currentTprzId = SelectedTprz == null ? (int?)null : SelectedTprz.Id;
            var currentTprmcpId = SelectedTprmcp == null ? (int?)null : SelectedTprmcp.Id;
            var currentTprmkId = SelectedTprmk == null ? (int?)null : SelectedTprmk.Id;
            var rows = await Task.Run(delegate { return _service.LoadTprzItems(tipsId); });
            _currentTprzCache = rows.ToList();
            _tprzSearchIndex[tipsId] = _currentTprzCache.ToList();
            ApplyTprzFilter(tprzIdToSelect.HasValue ? tprzIdToSelect : currentTprzId);
            if (SelectedTprz == null)
            {
                TprmcpItems.Clear();
                TprmkItems.Clear();
                SelectedTprmcp = null;
                SelectedTprmk = null;
                UpdateStatus();
                return;
            }

            var tprmcpIdToSelect = currentTprzId.HasValue && currentTprzId.Value == SelectedTprz.Id ? currentTprmcpId : null;
            var tprmkIdToSelect = currentTprzId.HasValue && currentTprzId.Value == SelectedTprz.Id ? currentTprmkId : null;
            await RefreshLeafTablesCoreAsync(SelectedTprz.Id, tprmcpIdToSelect, tprmkIdToSelect);
            UpdateStatus();
        }

        private async Task RefreshLeafTablesCoreAsync(int typeSizeId, int? tprmcpIdToSelect, int? tprmkIdToSelect)
        {
            var tprmkTask = Task.Run(delegate { return _service.LoadTprmkItems(typeSizeId); });
            var tprmcpTask = Task.Run(delegate { return _service.LoadTprmcpItems(typeSizeId); });
            await Task.WhenAll(tprmcpTask, tprmkTask);
            TprmkItems.Clear();
            foreach (var item in tprmkTask.Result) TprmkItems.Add(item);
            SelectedTprmk = tprmkIdToSelect.HasValue ? TprmkItems.FirstOrDefault(delegate(TprmkDirectoryItem item) { return item.Id == tprmkIdToSelect.Value; }) : TprmkItems.FirstOrDefault();
            TprmcpItems.Clear();
            foreach (var item in tprmcpTask.Result) TprmcpItems.Add(item);
            SelectedTprmcp = tprmcpIdToSelect.HasValue ? TprmcpItems.FirstOrDefault(delegate(TprmcpDirectoryItem item) { return item.Id == tprmcpIdToSelect.Value; }) : TprmcpItems.FirstOrDefault();
            UpdateStatus();
        }

        private void ApplySearchFilter()
        {
            if (IsBusy)
            {
                return;
            }

            var previousTipsId = SelectedTips == null ? (int?)null : SelectedTips.Id;
            var previousTprzId = SelectedTprz == null ? (int?)null : SelectedTprz.Id;
            ApplyTipsFilter(previousTipsId);

            if (SelectedTips == null)
            {
                _currentTprzCache = new List<TprzDirectoryItem>();
                TprzItems.Clear();
                TprmcpItems.Clear();
                TprmkItems.Clear();
                SelectedTprz = null;
                SelectedTprmcp = null;
                SelectedTprmk = null;
                return;
            }

            if (previousTipsId.HasValue && previousTipsId.Value == SelectedTips.Id)
            {
                ApplyTprzFilter(previousTprzId);
            }
        }

        private void ApplyTipsFilter(int? preferredTipsId)
        {
            var filteredItems = _tipsCache
                .Where(delegate(TipsDirectoryItem item) { return MatchesTipsSearch(item); })
                .ToList();

            TipsItems.Clear();
            foreach (var item in filteredItems)
            {
                TipsItems.Add(item);
            }

            SelectedTips = preferredTipsId.HasValue
                ? TipsItems.FirstOrDefault(delegate(TipsDirectoryItem item) { return item.Id == preferredTipsId.Value; })
                : TipsItems.FirstOrDefault();
        }

        private void ApplyTprzFilter(int? preferredTprzId)
        {
            var selectedTips = SelectedTips;
            var showAllTypeSizes = selectedTips != null && MatchesTipsOwnFields(selectedTips);
            var filteredItems = _currentTprzCache
                .Where(delegate(TprzDirectoryItem item) { return showAllTypeSizes || MatchesTprzOwnFields(item); })
                .ToList();

            TprzItems.Clear();
            foreach (var item in filteredItems)
            {
                TprzItems.Add(item);
            }

            SelectedTprz = preferredTprzId.HasValue
                ? TprzItems.FirstOrDefault(delegate(TprzDirectoryItem item) { return item.Id == preferredTprzId.Value; })
                : TprzItems.FirstOrDefault();
        }

        private bool MatchesTipsSearch(TipsDirectoryItem item)
        {
            if (item == null)
            {
                return false;
            }

            if (IsSearchEmpty())
            {
                return true;
            }

            if (MatchesTipsOwnFields(item))
            {
                return true;
            }

            List<TprzDirectoryItem> typeSizes;
            return _tprzSearchIndex.TryGetValue(item.Id, out typeSizes)
                   && typeSizes.Any(delegate(TprzDirectoryItem typeSize) { return MatchesTprzOwnFields(typeSize); });
        }

        private bool MatchesTipsOwnFields(TipsDirectoryItem item)
        {
            return MatchesSearchTokens(
                item == null ? null : item.Id.ToString(),
                item == null ? null : item.MeasurementAreaName,
                item == null ? null : item.InstrumentName,
                item == null ? null : item.TypeName,
                item == null ? null : item.CategoryName,
                item == null ? null : item.DesignName,
                item == null ? null : item.RegistryTypeNumber,
                item == null ? null : item.VniimsTypeCode.ToString(),
                item == null ? null : item.MetrControlCode,
                item == null ? null : item.Notes);
        }

        private bool MatchesTprzOwnFields(TprzDirectoryItem item)
        {
            return MatchesSearchTokens(
                item == null ? null : item.Id.ToString(),
                item == null ? null : item.RangeText,
                item == null ? null : item.AccuracyText,
                item == null ? null : item.CompletenessName,
                item == null ? null : item.RegistryTypeSizeNumber,
                item == null ? null : item.ServiceCode,
                item == null ? null : item.VniimsTypeSizeCode.ToString());
        }

        private bool IsSearchEmpty()
        {
            return string.IsNullOrWhiteSpace(SearchText);
        }

        private bool MatchesSearchTokens(params string[] values)
        {
            var tokens = GetSearchTokens();
            if (tokens.Length == 0)
            {
                return true;
            }

            var haystack = string.Join(" ", values.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })).ToUpperInvariant();
            return tokens.All(delegate(string token) { return haystack.IndexOf(token, StringComparison.Ordinal) >= 0; });
        }

        private string[] GetSearchTokens()
        {
            return (SearchText ?? string.Empty)
                .Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
                .Select(delegate(string token) { return token.Trim().ToUpperInvariant(); })
                .Where(delegate(string token) { return token.Length > 0; })
                .ToArray();
        }

        private void RefreshAsync()
        {
            RunBusyOperation(async delegate { await RefreshTipsCoreAsync(SelectedTips == null ? (int?)null : SelectedTips.Id); });
        }

        private void LoadTprzForSelection()
        {
            if (IsBusy) return;
            RunBusyOperation(async delegate
            {
                if (SelectedTips == null)
                {
                    _currentTprzCache = new List<TprzDirectoryItem>();
                    TprzItems.Clear(); TprmcpItems.Clear(); TprmkItems.Clear();
                    SelectedTprz = null; SelectedTprmcp = null; SelectedTprmk = null;
                    UpdateStatus();
                    return;
                }
                await RefreshTprzCoreAsync(SelectedTips.Id, null);
            });
        }

        private void LoadLeafTablesForSelection()
        {
            if (IsBusy) return;
            RunBusyOperation(async delegate
            {
                if (SelectedTprz == null)
                {
                    TprmcpItems.Clear(); TprmkItems.Clear(); SelectedTprmcp = null; SelectedTprmk = null; UpdateStatus(); return;
                }
                await RefreshLeafTablesCoreAsync(SelectedTprz.Id, null, null);
            });
        }

        private void RaiseCommandStates()
        {
            ((RelayCommand)AddTipsCommand).RaiseCanExecuteChanged(); ((RelayCommand)EditTipsCommand).RaiseCanExecuteChanged(); ((RelayCommand)DeleteTipsCommand).RaiseCanExecuteChanged(); ((RelayCommand)ManageTpvdklCommand).RaiseCanExecuteChanged();
            ((RelayCommand)AddTprzCommand).RaiseCanExecuteChanged(); ((RelayCommand)EditTprzCommand).RaiseCanExecuteChanged(); ((RelayCommand)DeleteTprzCommand).RaiseCanExecuteChanged();
            ((RelayCommand)AddTprmcpCommand).RaiseCanExecuteChanged(); ((RelayCommand)EditTprmcpCommand).RaiseCanExecuteChanged(); ((RelayCommand)DeleteTprmcpCommand).RaiseCanExecuteChanged();
            ((RelayCommand)AddTprmkCommand).RaiseCanExecuteChanged(); ((RelayCommand)EditTprmkCommand).RaiseCanExecuteChanged(); ((RelayCommand)DeleteTprmkCommand).RaiseCanExecuteChanged();
            ((RelayCommand)RefreshCommand).RaiseCanExecuteChanged();
        }

        private async void RunBusyOperation(Func<Task> operation)
        {
            try { await ExecuteBusyOperationAsync(operation); }
            catch (Exception ex) { IsBusy = false; _dialogService.ShowError(ex.Message); }
        }

        private async void RunMutationOperation(Func<Task> operation)
        {
            try { await ExecuteMutationOperationAsync(operation); }
            catch (Exception ex) { IsBusy = false; _dialogService.ShowError(ex.Message); }
        }

        private void UpdateStatus()
        {
            var searchText = string.IsNullOrWhiteSpace(SearchText) ? null : SearchText.Trim();
            StatusText = string.Format(
                "{0}TIPS: {1}/{2}. TPRZ: {3}/{4}. TPRMK: {5}. TPRMCP: {6}.",
                string.IsNullOrWhiteSpace(searchText) ? string.Empty : string.Format("Поиск: \"{0}\". ", searchText),
                TipsItems.Count,
                _tipsCache.Count,
                TprzItems.Count,
                _currentTprzCache.Count,
                TprmkItems.Count,
                TprmcpItems.Count);
        }

        private static TipsDirectoryItem CloneTips(TipsDirectoryItem source)
        {
            return new TipsDirectoryItem { Id = source.Id, MeasurementAreaId = source.MeasurementAreaId, MeasurementAreaName = source.MeasurementAreaName, InstrumentNameId = source.InstrumentNameId, InstrumentName = source.InstrumentName, CategoryId = source.CategoryId, CategoryName = source.CategoryName, DesignId = source.DesignId, DesignName = source.DesignName, TypeName = source.TypeName, ServiceLifeYears = source.ServiceLifeYears, RegistryPeriodMonths = source.RegistryPeriodMonths, RegistryTypeNumber = source.RegistryTypeNumber, VniimsTypeCode = source.VniimsTypeCode, MetrControlCode = source.MetrControlCode, Notes = source.Notes, IsSpecialPurpose = source.IsSpecialPurpose, IsMkPrimaryOnly = source.IsMkPrimaryOnly };
        }

        private static TprzDirectoryItem CloneTprz(TprzDirectoryItem source)
        {
            return new TprzDirectoryItem { Id = source.Id, TipsId = source.TipsId, CompletenessId = source.CompletenessId, CompletenessName = source.CompletenessName, RangeText = source.RangeText, AccuracyText = source.AccuracyText, VniimsTypeSizeCode = source.VniimsTypeSizeCode, RegistryTypeSizeNumber = source.RegistryTypeSizeNumber, ServiceCode = source.ServiceCode };
        }

        private static TprmcpDirectoryItem CloneTprmcp(TprmcpDirectoryItem source)
        {
            return new TprmcpDirectoryItem { Id = source.Id, TypeSizeId = source.TypeSizeId, CycleId = source.CycleId, CycleName = source.CycleName, GroupId = source.GroupId, GroupName = source.GroupName, PeriodMonths = source.PeriodMonths, Comment = source.Comment };
        }

        private static TprmkDirectoryItem CloneTprmk(TprmkDirectoryItem source)
        {
            return new TprmkDirectoryItem { Id = source.Id, TypeSizeId = source.TypeSizeId, VerificationTypeId = source.VerificationTypeId, VerificationTypeName = source.VerificationTypeName, OrganizationId = source.OrganizationId, OrganizationName = source.OrganizationName, QualificationId = source.QualificationId, QualificationName = source.QualificationName, GroupId = source.GroupId, GroupName = source.GroupName, PlaceId = source.PlaceId, PlaceName = source.PlaceName, Cost = source.Cost, ExtraCost = source.ExtraCost, RushMarkup = source.RushMarkup, TimeNormHours = source.TimeNormHours, TimeNormCode = source.TimeNormCode, VerifierCount = source.VerifierCount, VerificationCode = source.VerificationCode, NormDocHours = source.NormDocHours };
        }
    }
}
