using System; using System.Collections.ObjectModel; using System.Threading.Tasks; using System.Linq; using Xamarin.Forms; using Aurora.Services; using Aurora.Proto.General; using Aurora.Proto.Party; using Aurora.Proto.Events; using Aurora.Services.ClientService; using Aurora.Services.PlayerService; using Aurora.Services.EventManager; using Aurora.Models.Media; namespace Aurora.Design.Views.Party { //TODO refactor enum PartyState { SelectingHost, InParty, Hosting, Connecting, } public class PartyViewModel : BaseViewModel { private PartyState _state; private string _hostname; private ObservableCollection _members; private ObservableCollection _queue; private BaseMedia _selectedMedia; private PlayerService _player; private ClientService _client; public PartyViewModel() { this.JoinCommand = new Command(OnJoinExecute, CanJoinExecute); this.HostCommand = new Command(OnHostExecute, CanHostExecute); _members = new ObservableCollection(); _queue = new ObservableCollection(); SetState(PartyState.SelectingHost); this._player = PlayerService.Instance; PlayCommand = new Command(OnDoubleClickExecute, CanDoubleClickExecute); _client = ClientService.Instance; //Hook up event handler _client.EventReceived += this.OnEventReceived; } ~PartyViewModel() { //Task.Run(ServerService.Instance.Stop); } #region Properties /// /// Publc property for the members list /// /// public ObservableCollection Members { get { return _members; } set { SetProperty(ref _members, value); } } public bool IsSelectingHost { get { return _state == PartyState.SelectingHost; } } /// /// Public property indicating the state. /// /// public bool IsNotSelectingHost { get { return _state != PartyState.SelectingHost; } } /// /// Public property for queue item source /// /// public ObservableCollection Queue { get { return _queue; } set { if (value != _queue) { SetProperty(ref _queue, value); } } } /// /// Public property for the hostname bindable field /// /// public string Hostname { get { return _hostname; } set { SetProperty(ref _hostname, 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 property for join command /// /// public Command JoinCommand { get; set; } /// /// Pubic property for host command /// /// public Command HostCommand { get; set; } #endregion Properties #region Events public override void OnActive() { //TODO //If in party subscribe to events //If in party get events } public override void OnInactive() { //TODO //unsubscribe //stop getting events } /// /// An event handler for the client receiving update events /// /// The object that sent the event /// The event arguments public void OnEventReceived(object sender, EventReceivedEventArgs eventArgs) { switch (eventArgs.BaseEvent.DerivedEventCase) { case BaseEvent.DerivedEventOneofCase.None: { throw new InvalidOperationException(); } case BaseEvent.DerivedEventOneofCase.PartyMemberJoinedEvent: { PartyMemberJoinedEvent derivedEvent = eventArgs.BaseEvent.PartyMemberJoinedEvent; PartyMember member = new PartyMember { UserName = derivedEvent.Member.UserName, Id = derivedEvent.Member.Id, IpAddress = derivedEvent.Member.IpAddress, Port = derivedEvent.Member.Port }; Members.Add(member); break; } case BaseEvent.DerivedEventOneofCase.PartyMemberLeftEvent: { PartyMemberJoinedEvent derivedEvent = eventArgs.BaseEvent.PartyMemberJoinedEvent; var found = Members.Where(x => x.Id == derivedEvent.Member.Id); foreach (PartyMember member in found) { _members.Remove(member); } break; } case BaseEvent.DerivedEventOneofCase.MediaPlayingEvent: { MediaPlayingEvent derivedEvent = eventArgs.BaseEvent.MediaPlayingEvent; Play(derivedEvent); break; } case BaseEvent.DerivedEventOneofCase.MediaPausedEvent: { MediaPausedEvent derivedEvent = eventArgs.BaseEvent.MediaPausedEvent; StopPlaying(); break; } } } #endregion Events #region Commands private async void OnJoinExecute() { SetState(PartyState.Connecting); _client.Start(Hostname, SettingsService.Instance.DefaultPort.ToString()); await JoinParty(); //TODO add cancellation token try { SetState(PartyState.InParty); await _client.GetEvents().ConfigureAwait(true); } catch (Exception ex) { Console.WriteLine("Exception occurred while receiviing events: ", ex.Message); } } private bool CanJoinExecute() { return true; } private async void OnHostExecute() { //Change state SetState(PartyState.Connecting); ServerService.Instance.Start(); string localHost = ServerService.GetLocalIPAddress(); _client.IsHost = true; _client.Start(localHost, SettingsService.Instance.DefaultPort.ToString()); await JoinParty(); //TODO add cancellation token try { SetState(PartyState.Hosting); await _client.GetEvents().ConfigureAwait(true); } catch (Exception ex) { Console.WriteLine("Exception occurred while receiviing events: ", ex.Message); } } private bool CanHostExecute() { return true; } public override void OnPlayButtonExecute() { _player.Play(); switch (_player.PlaybackState) { case PlaybackState.Buffering: { _player.Play(); break; } case PlaybackState.Playing: { _player.Pause(); break; } } } public override bool CanPlayButtonExecute() { return this._state == PartyState.Hosting && this._player.IsLoaded; } public override bool CanNextButtonExecute() { return this._state == PartyState.Hosting; } public override bool CanPreviousButtonExecute() { return this._state == PartyState.Hosting; } /// /// On double click execute, fire media playing event /// public void OnDoubleClickExecute() { AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata; MediaPlayingEvent mediaPlaying = new MediaPlayingEvent() { Media = new RemoteMediaData() { Id = _selectedMedia.Id, Title = meta.Title, Artist = meta.Artist, Album = meta.Album, } }; EventManager.Instance.FireEvent(new BaseEvent() { MediaPlayingEvent = mediaPlaying }); } public bool CanDoubleClickExecute() { return this._state == PartyState.Hosting; } #endregion Commands #region Private Methods /// /// Join the remote party. /// /// private async Task JoinParty() { try { JoinPartyResponse resp = await _client.RemotePartyClient.JoinPartyAsync(new JoinPartyRequest { UserName = SettingsService.Instance.Username, }); SettingsService.Instance.ClientId = resp.ClientId; RefreshMembers(); //Subscribe to events SubscribeRequest req = new SubscribeRequest(); req.EventTypes.Add(EventType.PartyMemberJoined); req.EventTypes.Add(EventType.PartyMemberLeft); req.EventTypes.Add(EventType.MediaPlaying); req.EventTypes.Add(EventType.MediaStopped); if (!string.IsNullOrWhiteSpace(SettingsService.Instance.ClientId)) { req.ClientId = SettingsService.Instance.ClientId; } Console.WriteLine(string.Format("CLIENT {0} - SubscribeToEvents called from client with id", SettingsService.Instance.ClientId)); _client.RemoteEventClient.SubscribeToEvents(req); QueueResponse queueResponse = _client.RemotePartyClient.GetQueue(new Empty()); Queue.Clear(); //Convert received data to remote audio models foreach (RemoteMediaData data in queueResponse.MediaList) { //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.Id, meta, _client.RemotePartyClient); Queue.Add(remote); } } catch (Exception ex) { Console.WriteLine("Error subscribing to events: " + ex.Message); } } private async Task LeaveParty() { //Stop receiving events _client.StopEvents(); //Unsubscribe UnsubscribeAllRequest unsubscribeReq = new UnsubscribeAllRequest(); await _client.RemoteEventClient.UnsubscribeFromAllAsync(unsubscribeReq); //Leave party LeavePartyRequest leaveReq = new LeavePartyRequest(); await _client.RemotePartyClient.LeavePartyAsync(leaveReq); } /// /// Refresh members list. /// private void RefreshMembers() { MembersResponse response = _client.RemotePartyClient.GetPartyMembers(new Empty()); //Add members foreach (PartyMember member in response.Members) { Members.Add(member); } } private void SetState(PartyState state) { _state = state; OnPropertyChanged("IsSelectingHost"); OnPropertyChanged("IsNotSelectingHost"); } private async void Play(MediaPlayingEvent args) { //TODO this design assumes all played music is in a queue //TODO this is a slow design depending on size of queue if (_queue.Any((BaseMedia media) => media.Id == args.Media.Id)) { BaseMedia media = _queue.First((BaseMedia med) => med.Id == args.Media.Id); base.Media = media; await _player.LoadMedia(base.Media).ConfigureAwait(true); _player.Play(); } } private void StopPlaying() { _player.Pause(); } #endregion Private Methods } }