public ObservableMatch(Match match, TournamentContext context)
{
source = match;
OwningContext = context;
//Round doesn't change, initialize RoundFixed
var totalPlayerCount = context.Tournament.ParticipantsCount;
var lowerBoundExponent = Math.Floor(Math.Log(totalPlayerCount, 2));
var lowerBound = Math.Pow(2, lowerBoundExponent);
if (match.Round < 0 && totalPlayerCount > lowerBound && totalPlayerCount <= lowerBound + (lowerBound / 2))
{
Round = match.Round - 1;
}
else Round = match.Round;
//Check if station assignment data checks out. If not, clear the assignment
var player1Station = Player1 != null ? Player1.StationAssignment : default(string);
var player2Station = Player2 != null ? Player2.StationAssignment : default(string);
//If stations don't match, clear. Don't check completed matches because those will frequently have mismatching stations
if (State != "complete" && player1Station != player2Station) ClearStationAssignment();
//Listen for when properties changed to that changed events for the convenience properties can also be fired.
this.PropertyChanged += (sender, e) =>
{
switch (e.PropertyName)
{
case "Player1Id":
this.Raise("Player1", PropertyChanged);
this.Raise("PlayerCount", PropertyChanged);
if (Player1 != null) Player1.IsMissing = false; //When a player gets added to a match, clear their missing flag
break;
case "Player2Id":
this.Raise("Player2", PropertyChanged);
this.Raise("PlayerCount", PropertyChanged);
if (Player2 != null) Player2.IsMissing = false; //When a player gets added to a match, clear their missing flag
break;
case "Player1PrereqMatchId":
this.Raise("Player1PreviousMatch", PropertyChanged);
break;
case "Player2PrereqMatchId":
this.Raise("Player2PreviousMatch", PropertyChanged);
break;
case "StartedAt":
this.Raise("TimeSinceAvailable", PropertyChanged);
break;
case "State":
//Clear station assignments if match state changes
if (Player1 != null) Player1.ClearStationAssignment();
if (Player2 != null) Player2.ClearStationAssignment();
//If match state has changed to open, execute selected new match option
if (State == "open")
{
var option = GlobalSettings.Instance.SelectedNewMatchAction;
switch (option)
{
case NewMatchAction.AutoAssign:
//TODO: Consider using lock block here to prevent potential multithreaded assignment to the same station
var highestPriorityStation = Stations.Instance.GetBestNormalStation();
if (highestPriorityStation != null) AssignPlayersToStation(highestPriorityStation.Name);
break;
case NewMatchAction.Anywhere:
AssignPlayersToStation("Any");
break;
}
}
break;
}
};
var propertyChangedObs = Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(h => this.PropertyChanged += h, h => this.PropertyChanged -= h);
//The following will create an observable sequence that will raise an event either when player1 changes or when player1's station assignment status changes
var player1ChangedOrAssignmentChanged = propertyChangedObs.Where(a => a.EventArgs.PropertyName == "Player1")
.Select(_ =>
{
if (Player1 != null)
{
return Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(h =>
{
player1Queue.Enqueue(Player1);
Player1.PropertyChanged += h;
}, h =>
{
player1Queue.Dequeue().PropertyChanged -= h;
})
.Where(a => a.EventArgs.PropertyName == "IsAssignedToStation" || a.EventArgs.PropertyName == "StationAssignment")
.Select(_2 => EventArgs.Empty).StartWith(EventArgs.Empty);
}
else return Observable.Return(EventArgs.Empty);
}).Switch();
//Subscribe to above observable sequence to maintain the assignment state of the match
player1ChangedOrAssignmentChanged.Subscribe(_ =>
{
IsMatchInProgress = Player1 != null && Player1.IsAssignedToStation;
StationAssignment = Player1 == null ? null : Player1.StationAssignment;
});
//Forcibly raise player1 property notification to assign station status
this.Raise("Player1", PropertyChanged);
}