using System; using System.Collections.ObjectModel; using System.Threading.Tasks; using System.Threading; using System.Linq; using Xamarin.Forms; using Grpc.Core; using Aurora.Services; using Aurora.Proto.General; using Aurora.Proto.Party; using Aurora.Proto.Playback; using Aurora.Proto.Events; namespace Aurora.Design.Views.Party { enum PartyState { SelectingHost, InParty, Connecting, } public class PartyViewModel : BaseViewModel { private PartyState _state; private string _hostname; private ObservableCollection _members; //Client fields private Channel _channel; private RemotePartyService.RemotePartyServiceClient _remotePartyClient; private RemotePlaybackService.RemotePlaybackServiceClient _remotePlaybackClient; private RemoteEventService.RemoteEventServiceClient _remoteEventsClient; private Task _eventsTask; CancellationTokenSource _eventCancellationTokenSource; public PartyViewModel() { this.JoinCommand = new Command(OnJoinExecute, CanJoinExecute); this.HostCommand = new Command(OnHostExecute, CanHostExecute); _members = new ObservableCollection { new PartyMember{UserName = "asdf"} }; SetState(PartyState.SelectingHost); } ~PartyViewModel() { Task.Run(ServerService.Instance.Stop); } #region Properties public ObservableCollection Members { get { return _members; } set { SetProperty(ref _members, value); } } public bool IsSelectingHost { get { return _state == PartyState.SelectingHost; } } public bool IsNotSelectingHost { get { return _state != PartyState.SelectingHost; } } public Command JoinCommand { get; set; } public Command HostCommand { get; set; } public string Hostname { get { return _hostname; } set { SetProperty(ref _hostname, value); } } #endregion Properties #region Commands private void OnJoinExecute() { InitializeClients(Hostname, SettingsService.Instance.DefaultPort.ToString()); JoinParty(); SetState(PartyState.Connecting); } private bool CanJoinExecute() { return true; } private void OnHostExecute() { ServerService.Instance.Start(); string localHost = ServerService.GetLocalIPAddress(); InitializeClients(localHost, SettingsService.Instance.DefaultPort.ToString()); JoinParty(); //Change state SetState(PartyState.Connecting); } private bool CanHostExecute() { return true; } #endregion Commands #region Private Methods private void InitializeClients(string hostname, string port) { _channel = new Channel(string.Format("{0}:{1}", hostname, port), ChannelCredentials.Insecure); _remotePartyClient = new RemotePartyService.RemotePartyServiceClient(_channel); _remotePlaybackClient = new RemotePlaybackService.RemotePlaybackServiceClient(_channel); _remoteEventsClient = new RemoteEventService.RemoteEventServiceClient(_channel); //Assign but don't start task _eventsTask = new Task(GetEvents); _eventCancellationTokenSource = new CancellationTokenSource(); } /// /// Join the remote party. /// /// private async void JoinParty() { await _remotePartyClient.JoinPartyAsync(new JoinPartyRequest { UserName = SettingsService.Instance.Username, }); RefreshMembers(); //Subscribe to events SubscribeRequest req = new SubscribeRequest(); req.EventTypes.Add(EventType.PartyMemberJoined); req.EventTypes.Add(EventType.PartyMemberLeft); try { _remoteEventsClient.SubscribeToEvents(req); } catch (Exception ex) { Console.WriteLine("Error subscribing to events: " + ex.Message); } _eventsTask.Start(); } /// /// Refresh members list. /// private void RefreshMembers() { MembersResponse response = _remotePartyClient.GetPartyMembers(new Empty()); //Add members foreach (PartyMember member in response.Members) { Members.Add(member); } //Remove out of date members // foreach (PartyMember member in Members) // { // if (!response.Members.Contains(member)) // { // Members.Remove(member); // } // } } private void SetState(PartyState state) { _state = state; OnPropertyChanged("IsSelectingHost"); OnPropertyChanged("IsNotSelectingHost"); } /// /// Asynchronous function for processing events off of the event stream. /// /// private async void GetEvents() { using (AsyncServerStreamingCall eventStream = _remoteEventsClient.GetEvents(new Empty())) { while (!_eventCancellationTokenSource.Token.IsCancellationRequested) { if (await eventStream.ResponseStream.MoveNext(_eventCancellationTokenSource.Token)) { //Convert derived event type BaseEvent e = eventStream.ResponseStream.Current; switch (e.DerivedEventCase) { case BaseEvent.DerivedEventOneofCase.None: { throw new InvalidOperationException(); } case BaseEvent.DerivedEventOneofCase.PartyMemberJoinedEvent: { PartyMemberJoinedEvent derivedEvent = e.PartyMemberJoinedEvent; Members.Add(derivedEvent.Member); break; } case BaseEvent.DerivedEventOneofCase.PartyMemberLeftEvent: { PartyMemberJoinedEvent derivedEvent = e.PartyMemberJoinedEvent; var found = Members.Where(x => x.Id == derivedEvent.Member.Id); foreach (PartyMember member in found) { Members.Remove(member); } break; } } } } } } #endregion Private Methods } }