using System;
using System.Threading.Tasks;
using System.Threading;
using Grpc.Core;
using Aurora.Proto.Events;
using Aurora.Proto.Party;
using Aurora.Proto.Playback;
using Aurora.Proto.Sync;
using Aurora.Services.ClientService.Events;
using System.Collections.Generic;

namespace Aurora.Services.ClientService
{

    public class ClientService : BaseService<ClientService>
    {
        private RemotePartyService.RemotePartyServiceClient _remotePartyClient;
        private RemoteEventService.RemoteEventServiceClient _remoteEventsClient;
        private RemotePlaybackService.RemotePlaybackServiceClient _remotePlaybackClient;
        private RemoteSyncService.RemoteSyncServiceClient _remoteSyncClient;

        private Channel _channel;
        CancellationTokenSource _eventCancellationTokenSource;

        public ClientService()
        {
        }

        public MediaPausedEventHandler OnMediaPaused;
        public NewMediaPlayingEventHandler OnNewMediaPlaying;
        public PartyMemberJoinedEventHandler OnPartyMemberJoined;
        public PartyMemberLeftEventHandler OnPartyMemberLeft;
        public MediaResumedEventHandler OnMediaResumed;

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

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

        public RemotePlaybackService.RemotePlaybackServiceClient RemotePlaybackClient
        {
            get { return _remotePlaybackClient; }
        }

        public RemoteSyncService.RemoteSyncServiceClient RemoteSyncClient
        {
            get { return _remoteSyncClient; }
        }

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

        public bool IsHost { get; set; }

        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);
            _remotePlaybackClient = new RemotePlaybackService.RemotePlaybackServiceClient(_channel);
            _remoteSyncClient = new RemoteSyncService.RemoteSyncServiceClient(_channel);
        }

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

            _remotePartyClient = null;
            _remoteEventsClient = null;
            _remotePlaybackClient = null;
            _remoteSyncClient = null;
        }

        /// <summary>
        /// Asynchronous function for processing events off of the event stream.
        /// </summary>
        /// <returns></returns>
        public async Task GetEvents()
        {
            _eventCancellationTokenSource = new CancellationTokenSource();
            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))
                    {
                        try
                        {
                            BaseEvent e = new BaseEvent(eventStream.ResponseStream.Current);

                            Dictionary<BaseEvent.DerivedEventOneofCase, EventInfo> events = new Dictionary<BaseEvent.DerivedEventOneofCase, EventInfo>()
                        {
                            {BaseEvent.DerivedEventOneofCase.MediaPausedEvent, new EventInfo(this.OnMediaPaused, typeof(MediaPausedEventArgs))},
                            {BaseEvent.DerivedEventOneofCase.MediaResumedEvent, new EventInfo(this.OnMediaResumed, typeof(MediaResumedEventArgs))},
                            {BaseEvent.DerivedEventOneofCase.NewMediaPlayingEvent, new EventInfo(this.OnNewMediaPlaying, typeof(NewMediaPlayingEventArgs))},
                            {BaseEvent.DerivedEventOneofCase.PartyMemberJoinedEvent, new EventInfo(this.OnPartyMemberJoined, typeof(PartyMemberJoinedEventArgs))},
                            {BaseEvent.DerivedEventOneofCase.PartyMemberLeftEvent, new EventInfo(this.OnPartyMemberLeft, typeof(PartyMemberLeftEventArgs))}
                        };

                            events.TryGetValue(e.DerivedEventCase, out EventInfo eventInfo);

                            if (eventInfo != null && eventInfo.Handler != null)
                            {
                                eventInfo.Handler.DynamicInvoke(new object[] { this, Activator.CreateInstance(eventInfo.ArgsType, new object[] { e }) });
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("Exception while parsing event ---" + ex.Message);
                        }

                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(string.Format("EXCEPTION while parsing events --- " + ex.Message));
                }
            }
        }

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