using System; using System.Collections.ObjectModel; using System.Collections.Generic; using System.Threading.Tasks; using System.Threading; using System.Linq; using Xamarin.Forms; using Aurora.Proto.Party; using Aurora.Models.Media; using Aurora.Services.Client; using Aurora.Design.Views.Party.NewPartyDialog; using Aurora.Services.Settings; using Aurora.Services.Server; using Aurora.Services.EventManager; using Grpc.Core; namespace Aurora.Design.Views.Party { //TODO refactor enum PartyState { SelectingHost, InParty, Hosting, Connecting, } delegate void EventHandler(BaseEvent e); public class PartyViewModel : BaseViewModel { private PartyState _state; private string _hostname = ""; private ObservableCollection _members; private ObservableCollection _queue; private BaseMedia _selectedMedia; private ISettingsService _settingsService; private IClientService _clientService; private IServerService _serverService; private IEventManager _eventManager; private CancellationTokenSource _eventCancellationTokenSource; private Dictionary _eventHandlers; private int _selectedTabIndex; public PartyViewModel( ISettingsService settingsService, IServerService serverService, IEventManager eventManager, IClientService clientService) { _members = new ObservableCollection(); _queue = new ObservableCollection(); this._settingsService = settingsService; this._serverService = serverService; this._eventManager = eventManager; this._clientService = clientService; SetState(PartyState.SelectingHost); PlayCommand = new Command(OnDoubleClickCommandExecute, CanDoubleClickCommandExecute); LeavePartyCommand = new Command(OnLeavePartyCommandExecute, CanLeavePartyCommandExecute); //Setup event handlers _eventHandlers = new Dictionary() { {BaseEvent.DerivedEventOneofCase.MediaPausedEvent, this.OnRemoteMediaPaused}, {BaseEvent.DerivedEventOneofCase.MediaResumedEvent, this.OnRemoteMediaResumed}, {BaseEvent.DerivedEventOneofCase.NewMediaPlayingEvent, this.OnNewRemoteMediaPlaying}, {BaseEvent.DerivedEventOneofCase.MemberCreatedEvent, this.OnPartyMemberJoined}, {BaseEvent.DerivedEventOneofCase.MemberDeletedEvent, this.OnPartyMemberLeft} }; } #region Properties public int SelectedTabIndex { get { return _selectedTabIndex; } set { SetProperty(ref _selectedTabIndex, value); } } /// /// Publc property for the members list /// /// public ObservableCollection Members { get { return _members; } set { SetProperty(ref _members, value); } } /// /// Public property for queue item source /// /// public ObservableCollection Queue { get { return _queue; } set { if (value != _queue) { SetProperty(ref _queue, value); } } } /// /// Public property for the currently selected song. /// /// public BaseMedia SelectedSong { get { return _selectedMedia; } set { SetProperty(ref _selectedMedia, value); } } /// /// Public property for playing media /// /// public Command PlayCommand { get; private set; } public Command LeavePartyCommand { get; private set; } #endregion Properties #region Events /// /// Called by framework when view becomes active /// /// public override async Task OnActive() { OnPropertyChanged("SelectedTabIndex"); if (this._state == PartyState.Hosting || this._state == PartyState.InParty) { await this.GetEvents().ConfigureAwait(false); } else { //Open host selection modal NewPartyDialogViewModel vm = new NewPartyDialogViewModel(); ConnectionDetails details = new ConnectionDetails(); vm.Finish = () => { this.HideModal(); details = vm.ReturnObject as ConnectionDetails; _hostname = details.HostName; switch (details.ConnectionType) { case ConnectionType.Host: { OnHostCommandExecute(); break; } case ConnectionType.Join: { OnJoinCommandExecute(); break; } } }; this.ShowModal(typeof(NewPartyDialog.NewPartyDialog), vm); } } /// /// Called by framework when view becomes inactive /// /// public override Task OnInactive() { if(this._eventCancellationTokenSource != null){ this._eventCancellationTokenSource.Cancel(); } return Task.FromResult(null); } /// /// Remote media paused event /// /// /// public void OnRemoteMediaPaused(BaseEvent e) { StopPlaying(); } /// /// Remote playing new media event /// /// /// public void OnNewRemoteMediaPlaying(BaseEvent e) { PlayFromBeginning(GetMediaFromQueue(e.NewMediaPlayingEvent.Media.Name)); } /// /// Remote resumed playing event /// /// /// public void OnRemoteMediaResumed(BaseEvent e) { PlayResume(); } /// /// Member joined party event /// /// /// public void OnPartyMemberJoined(BaseEvent e) { Members.Add(e.MemberCreatedEvent.Member); } /// /// Member left party event /// /// /// public void OnPartyMemberLeft(BaseEvent e) { var found = Members.Where(x => x.Name == e.MemberDeletedEvent.MemberName); foreach (Member member in found) { _members.Remove(member); } } #endregion Events #region Commands private async void OnJoinCommandExecute() { SetState(PartyState.Connecting); _clientService.Start(_hostname, this._settingsService.DefaultPort.ToString()); await JoinParty(false); //TODO add cancellation token try { SetState(PartyState.InParty); await GetEvents().ConfigureAwait(true); } catch (Exception ex) { Console.WriteLine("Exception occurred while receiviing events: ", ex.Message); } } private bool CanJoinCommandExecute() { return true; } private async void OnHostCommandExecute() { //Change state SetState(PartyState.Connecting); _serverService.Start("test", "asdf"); string localHost = ServerService.GetLocalIPAddress(); _clientService.Start(localHost, this._settingsService.DefaultPort.ToString()); await JoinParty(true); //TODO add cancellation token try { SetState(PartyState.Hosting); await GetEvents().ConfigureAwait(false); } catch (Exception ex) { Console.WriteLine("Exception occurred while receiviing events: ", ex.Message); } } private bool CanHostCommandExecute() { return true; } private async void OnLeavePartyCommandExecute() { await _clientService.RemotePartyServiceClient.DeleteMemberAsync(new DeleteMemberRequest() { Name = _settingsService.ClientName }); } private bool CanLeavePartyCommandExecute() { return (this._state == PartyState.InParty || this._state == PartyState.Hosting) ? true : false; } public override void OnPlayButtonCommandExecute() { if (base.IsPlaying()) { //Fire play stopped event AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata; MediaPausedEvent mediaPaused = new MediaPausedEvent(); _eventManager.FireEvent(new BaseEvent() { MediaPausedEvent = mediaPaused }); } else { //Fire play resume event AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata; MediaResumedEvent mediaResumed = new MediaResumedEvent(); _eventManager.FireEvent(new BaseEvent() { MediaResumedEvent = mediaResumed }); } } public override bool CanPlayButtonCommandExecute() { return this._state == PartyState.Hosting; } public override bool CanNextButtonCommandExecute() { return this._state == PartyState.Hosting; } public override bool CanPreviousButtonCommandExecute() { return this._state == PartyState.Hosting; } /// /// On double click execute, fire media playing event /// public void OnDoubleClickCommandExecute() { //Fire Playing event AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata; NewMediaPlayingEvent mediaPlaying = new NewMediaPlayingEvent() { Media = new Media() { //TODO need full resource name Name = _selectedMedia.Id, Title = meta.Title, Artist = meta.Artist, Album = meta.Album, } }; _eventManager.FireEvent(new BaseEvent() { NewMediaPlayingEvent = mediaPlaying }); } public bool CanDoubleClickCommandExecute() { return this._state == PartyState.Hosting; } #endregion Commands #region Private Methods /// /// Join the remote party. /// /// private async Task JoinParty(bool asHost) { try { Member resp = await _clientService.RemotePartyServiceClient.CreateMemberAsync(new CreateMemberRequest { Member = new Member() { UserName = this._settingsService.Username, } }); this._settingsService.ClientName = resp.Name; await RefreshMembers(); //Subscribe to events await SubscribeToEvents(); Queue.Clear(); ListMediaResponse mediaResponse = await _clientService.RemotePartyServiceClient.ListMediaAsync(new ListMediaRequest() { PageSize = 50, Parent = "TODO" }); //Convert received data to remote audio models foreach (Media data in mediaResponse.Media) { //Assign received metadata (since this can't be aquired from a file) AudioMetadata meta = new AudioMetadata(); meta.Title = data.Title; meta.Album = data.Album; meta.Artist = data.Artist; meta.Duration = data.Duration; RemoteAudio remote = new RemoteAudio(data.Name, asHost, meta, _clientService.RemotePartyServiceClient); Queue.Add(remote); OnPropertyChanged("Queue"); } } catch (Exception ex) { Console.WriteLine("Error subscribing to events: " + ex.Message); } } private async Task LeaveParty() { //Stop receiving events // _client.StopEvents(); //Unsubscribe await UnsubscribeFromEvents(); //Leave party DeleteMemberRequest req = new DeleteMemberRequest() { Name = _settingsService.ClientName }; await _clientService.RemotePartyServiceClient.DeleteMemberAsync(req); } private async Task SubscribeToEvents() { CreateEventSubscriptionListRequest req = new CreateEventSubscriptionListRequest(); req.Parent = this._settingsService.ClientName; req.EventSubscriptions.Add(new EventSubscription() { Type = EventType.MemberCreated }); req.EventSubscriptions.Add(new EventSubscription() { Type = EventType.MemberDeleted }); req.EventSubscriptions.Add(new EventSubscription() { Type = EventType.MediaPlaying }); req.EventSubscriptions.Add(new EventSubscription() { Type = EventType.MediaStopped }); Console.WriteLine(string.Format("CLIENT {0} - SubscribeToEvents called from client with id", this._settingsService.ClientName)); await _clientService.RemotePartyServiceClient.CreateEventSubscriptionListAsync(req); } private async Task UnsubscribeFromEvents() { DeleteAllEventSubscriptionsRequest unsubscribeReq = new DeleteAllEventSubscriptionsRequest(); await _clientService.RemotePartyServiceClient.DeleteAllEventSubscriptionsAsync(unsubscribeReq); } /// /// Refresh members list. /// private async Task RefreshMembers() { Members.Clear(); ListMembersResponse response = await _clientService.RemotePartyServiceClient.ListMembersAsync( new ListMembersRequest() { Parent = "TODO", PageSize = 50, }); //Add members foreach (Member member in response.Members) { Members.Add(member); } } private void SetState(PartyState state) { _state = state; OnPropertyChanged("IsSelectingHost"); OnPropertyChanged("IsNotSelectingHost"); } private BaseMedia GetMediaFromQueue(string Id) { if (_queue.Any((BaseMedia media) => media.Id == Id)) { BaseMedia media = _queue.First((BaseMedia med) => med.Id == Id); return media; } else { return null; } } private void PlayFromBeginning(BaseMedia args) { base.ChangePlayerState(args, Main.PlayAction.Play); } private void PlayResume() { base.ChangePlayerState(null, Main.PlayAction.Resume); } private void StopPlaying() { base.ChangePlayerState(null, Main.PlayAction.Pause); } /// /// Asynchronous function for processing events off of the event stream. /// /// public async Task GetEvents() { _eventCancellationTokenSource = new CancellationTokenSource(); string clientName = this._settingsService.ClientName; Console.WriteLine(string.Format("CLIENT {0} - GetEvents called from client with id", clientName)); using (AsyncServerStreamingCall eventStream = _clientService.RemotePartyServiceClient .GetEvents(new GetEventsRequest { Parent = this._settingsService.ClientName })) { try { while (!_eventCancellationTokenSource.Token.IsCancellationRequested && await eventStream.ResponseStream.MoveNext(_eventCancellationTokenSource.Token)) { try { BaseEvent e = new BaseEvent(eventStream.ResponseStream.Current); _eventHandlers.TryGetValue(e.DerivedEventCase, out EventHandler handler); if (handler != null && handler != null) { handler.Invoke(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)); } } } #endregion Private Methods } }