using Pulsar.Client.Helper;
using Pulsar.Client.Networking;
using Pulsar.Common.Enums;
using Pulsar.Common.Messages;
using System;
using System.Threading;

namespace Pulsar.Client.User
{
    /// <summary>
    /// Provides user activity detection and sends <see cref="SetUserStatus"/> messages on change.
    /// </summary>
    public class ActivityDetection : IDisposable
    {
        /// <summary>
        /// Stores the last user status to detect changes.
        /// </summary>
        private UserStatus _lastUserStatus;

        /// <summary>
        /// The client to use for communication with the server.
        /// </summary>
        private readonly PulsarClient _client;

        /// <summary>
        /// Create a <see cref="_token"/> and signals cancellation.
        /// </summary>
        private readonly CancellationTokenSource _tokenSource;

        /// <summary>
        /// The token to check for cancellation.
        /// </summary>
        private readonly CancellationToken _token;

        /// <summary>
        /// Initializes a new instance of <see cref="ActivityDetection"/> using the given client.
        /// </summary>
        /// <param name="client">The name of the mutex.</param>
        public ActivityDetection(PulsarClient client)
        {
            _client = client;
            _tokenSource = new CancellationTokenSource();
            _token = _tokenSource.Token;
            client.ClientState += OnClientStateChange;
        }

        private void OnClientStateChange(Networking.Client s, bool connected)
        {
            // reset user status
            if (connected)
                _lastUserStatus = UserStatus.Active;
        }

        /// <summary>
        /// Starts the user activity detection.
        /// </summary>
        public void Start()
        {
            new Thread(UserActivityThread).Start();
        }

        /// <summary>
        /// Checks for user activity changes sends <see cref="SetUserStatus"/> to the <see cref="_client"/> on change.
        /// </summary>
        private void UserActivityThread()
        {
            try
            {
                if (IsUserIdle())
                {
                    if (_lastUserStatus != UserStatus.Idle)
                    {
                        _lastUserStatus = UserStatus.Idle;
                        _client.Send(new SetUserStatus { Message = _lastUserStatus });
                    }
                }
                else
                {
                    if (_lastUserStatus != UserStatus.Active)
                    {
                        _lastUserStatus = UserStatus.Active;
                        _client.Send(new SetUserStatus { Message = _lastUserStatus });
                    }
                }
            }
            catch (Exception e) when (e is NullReferenceException || e is ObjectDisposedException)
            {
            }
        }

        /// <summary>
        /// Determines whether the user is idle if the last user input was more than 10 minutes ago.
        /// </summary>
        /// <returns><c>True</c> if the user is idle, else <c>false</c>.</returns>
        private bool IsUserIdle()
        {
            var ticks = Environment.TickCount;

            var idleTime = ticks - NativeMethodsHelper.GetLastInputInfoTickCount();

            idleTime = ((idleTime > 0) ? (idleTime / 1000) : 0);

            return (idleTime > 600); // idle for 10 minutes
        }

        public static string UserIdleTime()
        {
            var ticks = Environment.TickCount;
            var idleTime = ticks - NativeMethodsHelper.GetLastInputInfoTickCount();
            idleTime = ((idleTime > 0) ? (idleTime / 1000) : 0);
            return TimeSpan.FromSeconds(idleTime).ToString(@"hh\:mm\:ss");
        }

        /// <summary>
        /// Disposes all managed and unmanaged resources associated with this activity detection service.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                _client.ClientState -= OnClientStateChange;
                _tokenSource.Cancel();
                _tokenSource.Dispose();
            }
        }
    }
}
