using System;
using System.Threading.Tasks;
using System.Threading;
using Grpc.Core;
using Aurora.Proto.Events;
using Aurora.Proto.Party;
using Aurora.Services.ClientService;

namespace Aurora.Services.ClientService
{
    public class ClientService : BaseService<ClientService>
    {
        private RemotePartyService.RemotePartyServiceClient _remotePartyClient;
        private RemoteEventService.RemoteEventServiceClient _remoteEventsClient;
        private Channel _channel;
        CancellationTokenSource _eventCancellationTokenSource;

        public ClientService()
        {

        }

        public EventReceivedEventHandler EventReceived;

        public RemotePartyService.RemotePartyServiceClient RemotePartyClient
        {
            get
            {
                return _remotePartyClient;
            }
        }

        public RemoteEventService.RemoteEventServiceClient RemoteEventClient
        {
            get { return _remoteEventsClient; }
        }

        public bool IsStarted
        {
            get
            {
                return _remoteEventsClient != null &&
                    _remotePartyClient != null;
            }
        }

        public void Start(string hostname, string port)
        {
            _channel = new Channel(string.Format("{0}:{1}", hostname, port), ChannelCredentials.Insecure);

            _remotePartyClient = new RemotePartyService.RemotePartyServiceClient(_channel);
            _remoteEventsClient = new RemoteEventService.RemoteEventServiceClient(_channel);

            //Assign but don't start task
            _eventCancellationTokenSource = new CancellationTokenSource();
        }

        public async void Close()
        {
            _eventCancellationTokenSource.Cancel();
            await _channel.ShutdownAsync();

            _remotePartyClient = null;
            _remoteEventsClient = null;
        }

        /// <summary>
        /// Asynchronous function for processing events off of the event stream.
        /// </summary>
        /// <returns></returns>
        public async Task GetEvents()
        {
            string clientId = SettingsService.Instance.ClientId;
            Console.WriteLine(string.Format("CLIENT {0} - GetEvents called from client with id", clientId));
            using (AsyncServerStreamingCall<BaseEvent> eventStream = _remoteEventsClient
                .GetEvents(new EventsRequest { ClientId = SettingsService.Instance.ClientId }))
            {
                try
                {
                    while (!_eventCancellationTokenSource.Token.IsCancellationRequested &&
                        await eventStream.ResponseStream.MoveNext(_eventCancellationTokenSource.Token))
                    {
                        BaseEvent e = new BaseEvent(eventStream.ResponseStream.Current);
                        if (this.EventReceived != null)
                        {
                            this.EventReceived.Invoke(this, new EventReceivedEventArgs(e));
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(string.Format("EXCEPTION --- " + ex.Message));
                }
            }
        }

        public void StopEvents()
        {
            if (!_eventCancellationTokenSource.IsCancellationRequested)
            {
                _eventCancellationTokenSource.Cancel();
            }
        }
    }
}