using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;

using FasdDesktopUi.Basics.Models;

using C4IT.FASD.Cockpit.Communication;
using C4IT.Logging;
using static C4IT.Logging.cLogManager;
using C4IT.MultiLanguage;
using C4IT.FASD.Base;

namespace FasdDesktopUi.Basics.UserControls
{
    public partial class SearchBar : UserControl, IBlurInvoker
    {
        private const int constTimeBetweenKeystrokes = 250;

        #region Propeties
        public enum eSearchStatus { message, active, fixedResult };
        public eSearchStatus SearchStatus { get; private set; } = eSearchStatus.message;

        #region Search Value Property

        private readonly DispatcherTimer _searchTimer = new DispatcherTimer();

        private static CancellationTokenSource _currentSearchTaskToken = null;
        private static Guid? _currentSearchTaskId = null;
        private static readonly object _currentSearchTaskLock = new object();

        private string _searchValue = null;
        public string SearchValue
        {
            get => _searchValue;
            set
            {
                if (value != _searchValue)
                {
                    _searchValue = value;
                    if (SearchStatus == eSearchStatus.active)
                        HandleSearchValueChanged();
                }
            }
        }

        private readonly Dictionary<enumF4sdSearchResultClass, UIElement> _searchIcons = null;

        #endregion

        public event EventHandler<string> SearchValueChanged;

        public delegate Task ChangedSearchValueDelegate(cFilteredResults results);

        public ChangedSearchValueDelegate ChangedSearchValue { get; set; } = null;

        public Action CancledSearchAction { get; set; }

        public bool BlurInvoker_IsActive => IsVisible;

        #endregion

        #region Dependency Propertys

        public double SearchButtonSize
        {
            get { return (double)GetValue(SearchButtonSizeProperty); }
            set { SetValue(SearchButtonSizeProperty, value); }
        }

        public static readonly DependencyProperty SearchButtonSizeProperty =
            DependencyProperty.Register("SearchButtonSize", typeof(double), typeof(SearchBar), new PropertyMetadata(30.0));

        public double CloseButtonSize
        {
            get { return (double)GetValue(CloseButtonSizeProperty); }
            set { SetValue(CloseButtonSizeProperty, value); }
        }

        public static readonly DependencyProperty CloseButtonSizeProperty =
            DependencyProperty.Register("CloseButtonSize", typeof(double), typeof(SearchBar), new PropertyMetadata(30.0));
        #endregion

        public SearchBar()
        {
            InitializeComponent();

            _searchIcons = new Dictionary<enumF4sdSearchResultClass, UIElement>()
                        {
                            { enumF4sdSearchResultClass.Phone, PhoneCallIndicator },
                            { enumF4sdSearchResultClass.User, UserCallIndicator },
                            { enumF4sdSearchResultClass.Computer, ComputerCallIndicator }
                        };
        }

        public void BlurInvoker_IsActiveChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            BlurInvoker.InvokeVisibilityChanged(this, new EventArgs());

            if (e.NewValue is bool newVisible)
            {
                if (newVisible == false)
                {
                    SearchTextBox.IsEnabled = true;
                    SearchButton.Visibility = Visibility.Visible;
                    WarningIndicator.Visibility = Visibility.Collapsed;
                    TicketOverview.Instance?.ResetSelection();
                    foreach (var Entry in _searchIcons.Values)
                        Entry.Visibility = Visibility.Collapsed;

                }
                SetApiStatus();
            }

            if (Visibility == Visibility.Visible)
            {
                var _h = Dispatcher.BeginInvoke((Action)delegate { Keyboard.Focus(SearchTextBox); }, DispatcherPriority.Render);
            }
        }

        public void Clear()
        {
            SearchStatus = eSearchStatus.message;
            SearchTextBox.Text = null;
        }

        public void ActivateManualSearch()
        {
            SearchStatus = eSearchStatus.active;
            SearchButton.Visibility = Visibility.Visible;
            WarningIndicator.Visibility = Visibility.Collapsed;
            foreach (var Entry in _searchIcons.Values)
                Entry.Visibility = Visibility.Collapsed;

            SearchTextBox.Text = null;
            SearchTextBox.IsEnabled = true;
            SearchTextBox.Focus();
            Keyboard.Focus(SearchTextBox);
        }

        public void SetSearchText(string search)
        {
            SearchStatus = eSearchStatus.active;
            SearchTextBox.Text = search;
            SearchTextBox.CaretIndex = search.Length;
            SearchTextBox.Focus();
            Keyboard.Focus(SearchTextBox);
        }

        internal void SetSpinnerVisibility(Visibility visibility)
        {
            Dispatcher.Invoke(() => SearchSpinner.Visibility = visibility);
        }

        public async Task SetFixedSearchResultAsync(enumF4sdSearchResultClass Class, string search, cFilteredResults result)
        {
            SearchStatus = eSearchStatus.fixedResult;
            SearchTextBox.IsEnabled = false;
            SearchButton.Visibility = Visibility.Collapsed;
            WarningIndicator.Visibility = Visibility.Collapsed;

            foreach (var entryIcon in _searchIcons)
            {
                if (Class == entryIcon.Key)
                    entryIcon.Value.Visibility = Visibility.Visible;
                else
                    entryIcon.Value.Visibility = Visibility.Collapsed;
            }
            SearchTextBox.Text = search;

            if (ChangedSearchValue != null)
                await ChangedSearchValue.Invoke(result);
        }

        public void SetApiStatus()
        {
            try
            {
                var Status = cConnectionStatusHelper.Instance?.ApiConnectionStatus;
                if (Status == null)
                    return;

                bool IsEnabled = false;
                string txt = null;
                switch (Status)
                {
                    case cConnectionStatusHelper.enumOnlineStatus.online:
                        IsEnabled = true;
                        break;
                    case cConnectionStatusHelper.enumOnlineStatus.connectionError:
                        txt = cMultiLanguageSupport.GetItem("Searchbar.Status.ConnectError");
                        break;
                    case cConnectionStatusHelper.enumOnlineStatus.incompatibleServerVersion:
                        txt = cMultiLanguageSupport.GetItem("Searchbar.Status.IncompatibleVersion");
                        break;
                    case cConnectionStatusHelper.enumOnlineStatus.serverStarting:
                        txt = cMultiLanguageSupport.GetItem("Searchbar.Status.ServerStarting");
                        break;
                    case cConnectionStatusHelper.enumOnlineStatus.serverNotConfigured:
                        txt = cMultiLanguageSupport.GetItem("Searchbar.Status.ServerNotConfigured");
                        break;
                    case cConnectionStatusHelper.enumOnlineStatus.illegalConfig:
                        txt = cMultiLanguageSupport.GetItem("Searchbar.Status.IllegalConfig");
                        break;
                    case cConnectionStatusHelper.enumOnlineStatus.unauthorized:
                        txt = cMultiLanguageSupport.GetItem("Searchbar.Status.Unauthorized");
                        break;
                    default:
                        txt = cMultiLanguageSupport.GetItem("Searchbar.Status.Offline");
                        break;
                }

                if (!IsEnabled)
                {
                    SearchStatus = eSearchStatus.message;
                    SearchTextBox.Text = txt;
                    SearchTextBox.IsEnabled = false;
                    WarningIndicator.Visibility = Visibility.Visible;
                    SearchButton.Visibility = Visibility.Collapsed;
                    foreach (var Entry in _searchIcons.Values)
                        Entry.Visibility = Visibility.Collapsed;
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
            }
        }

        private async Task CancelRunningSearchTaskAsync()
        {
            // cancel the search task, in case of a running search task
            lock (_currentSearchTaskLock)
            {
                if (_currentSearchTaskToken != null)
                {
                    LogEntry("Canceling current running search...");
                    _currentSearchTaskToken.Cancel(true);
                    _currentSearchTaskToken = null;
                }
            }

            // send a stop to the server, in case of a running search task
            Guid? currentTaskId = null;
            lock (_currentSearchTaskLock)
            {
                if (_currentSearchTaskId != null)
                {
                    currentTaskId = _currentSearchTaskId;
                    _currentSearchTaskId = null;
                }
            }
            if (currentTaskId != null)
            {
                LogEntry("Sending a search stop...");
                await cFasdCockpitCommunicationBase.Instance.GetSearchResultsStop((Guid)currentTaskId, CancellationToken.None);
            }

        }

        private async void UpdateSearchResultsTick(object sender, EventArgs e)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }

            try
            {
                _searchTimer.Stop();

                await CancelRunningSearchTaskAsync();

                lock (_currentSearchTaskLock)
                {
                    LogEntry("Start running new search...");
                    SearchValueChanged?.Invoke(this, SearchValue);
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }
        }

        private void HandleSearchValueChanged()
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }

            try
            {
                if (!SearchTextBox.IsEnabled)
                    return;

                if (_searchTimer.IsEnabled)
                    _searchTimer.Stop();

                _searchTimer.Start();

                _ = Task.Run(async () => { await CancelRunningSearchTaskAsync(); });
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }
        }

        private void SearchButton_Click(object sender, InputEventArgs e)
        {
            SearchTextBox.Focus();
        }

        #region CancledSearch Event

        public static readonly RoutedEvent CancledSearchEvent = EventManager.RegisterRoutedEvent("CancledSearch", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SearchBar));

        public event RoutedEventHandler CancledSearch
        {
            add { AddHandler(CancledSearchEvent, value); }
            remove { RemoveHandler(CancledSearchEvent, value); }
        }

        private void RaiseCancledSearchEvent()
        {
            RoutedEventArgs newEventArgs = new RoutedEventArgs(CancledSearchEvent);
            RaiseEvent(newEventArgs);
        }

        #endregion

        private void CloseButton_Click(object sender, InputEventArgs e)
        {
            RaiseCancledSearchEvent();

            if (cConnectionStatusHelper.Instance?.ApiConnectionStatus == cConnectionStatusHelper.enumOnlineStatus.online)
                SearchTextBox.Text = null;

            CancledSearchAction?.Invoke();
        }

        private void SearchBar_Loaded(object sender, RoutedEventArgs e)
        {
            _searchTimer.Stop();
            _searchTimer.Interval = TimeSpan.FromMilliseconds(constTimeBetweenKeystrokes);
            _searchTimer.Tick += UpdateSearchResultsTick;
            BackgroundBorder.CornerRadius = new CornerRadius(BackgroundBorder.ActualHeight / 2.0);
        }
    }
}
