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; CancellationTokenSource _eventCancellationTokenSource; public PartyViewModel() { this.JoinCommand = new Command(OnJoinExecute, CanJoinExecute); this.HostCommand = new Command(OnHostExecute, CanHostExecute); _members = new ObservableCollection(); 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 _eventCancellationTokenSource = new CancellationTokenSource(); } /// /// Join the remote party. /// /// private async void JoinParty() { JoinPartyResponse resp = await _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); if (!string.IsNullOrWhiteSpace(SettingsService.Instance.ClientId)) { req.ClientId = SettingsService.Instance.ClientId; } try { Console.WriteLine(string.Format("CLIENT {0} - SubscribeToEvents called from client with id", SettingsService.Instance.ClientId)); _remoteEventsClient.SubscribeToEvents(req); } catch (Exception ex) { Console.WriteLine("Error subscribing to events: " + ex.Message); } GetEvents(); } /// /// 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"); } private void AddMember(PartyMember member) { Members.Add(member); } /// /// Asynchronous function for processing events off of the event stream. /// /// private async void GetEvents() { Console.WriteLine(string.Format("CLIENT {0} - GetEvents called from client with id", SettingsService.Instance.ClientId)); using (AsyncServerStreamingCall eventStream = _remoteEventsClient .GetEvents(new EventsRequest { ClientId = SettingsService.Instance.ClientId })) { while (!_eventCancellationTokenSource.Token.IsCancellationRequested && await eventStream.ResponseStream.MoveNext(_eventCancellationTokenSource.Token)) { Console.WriteLine(string.Format("CLIENT {0} - Event received for client with id", SettingsService.Instance.ClientId)); try { //Convert derived event type BaseEvent e = new BaseEvent(eventStream.ResponseStream.Current); switch (e.DerivedEventCase) { case BaseEvent.DerivedEventOneofCase.None: { throw new InvalidOperationException(); } case BaseEvent.DerivedEventOneofCase.PartyMemberJoinedEvent: { PartyMemberJoinedEvent derivedEvent = e.PartyMemberJoinedEvent; PartyMember member = new PartyMember { UserName = derivedEvent.Member.UserName, Id = derivedEvent.Member.Id, IpAddress = derivedEvent.Member.IpAddress, Port = derivedEvent.Member.Port }; AddMember(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; } } } catch (Exception ex) { Console.WriteLine(string.Format("EXCEPTION --- " + ex.Message)); } OnPropertyChanged("Members"); } } } #endregion Private Methods } }