using DominionBase;
using DominionBase.Cards;
using DominionBase.Enums;
using DominionBase.Piles;
using DominionBase.Players;
using DominionBase.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using CtrlOrientation = System.Windows.Controls.Orientation;
namespace Dominion.NET_WPF
{
///
/// Interaction logic for CardMatControl.xaml
///
public partial class CardMatControl : UserControl
{
public static readonly DependencyProperty SplitAtProperty =
DependencyProperty.Register("SplitAt", typeof(Func), typeof(CardMatControl), new PropertyMetadata(null));
public Func SplitAt
{
get { return (Func)GetValue(SplitAtProperty); }
set
{
SetValue(SplitAtProperty, value);
UpdateCardDisplay();
}
}
public static readonly DependencyProperty MinStackWidthProperty =
DependencyProperty.Register("MinStackWidth", typeof(double), typeof(CardMatControl), new PropertyMetadata(0d));
public double MinStackWidth
{
get { return (double)GetValue(MinStackWidthProperty); }
set
{
SetValue(MinStackWidthProperty, value);
UpdateCardDisplay();
}
}
private CardSize _cardSize = CardSize.Text;
private CardMat _cardPile = null;
private Dictionary _tokenDict;
private const int GlowSize = 20;
private string _pileName = "Pile Name";
private bool _displayEmptyPile;
private IPlayer _player;
private PhaseEnum _playerPhase = PhaseEnum.Action;
private PlayerMode _playerMode = PlayerMode.Waiting;
private bool _isClickable;
public CardMatControl()
{
InitializeComponent();
CardSize = CardSize.Medium;
IsClickable = false;
var gs = GlowSize / 2d;
wpCardCollections.Margin = new Thickness(gs, gs, gs, 0);
wpCardCollections2.Margin = new Thickness(gs, gs, gs, 0);
}
public IDisplayable ClickedCard { get; } = null;
public CtrlOrientation Orientation { get; set; } = CtrlOrientation.Horizontal;
public HorizontalAlignment CardHorizontalAlignment { get; set; } = HorizontalAlignment.Left;
public bool ExactCount { get; set; }
public bool IsCardsVisible { get; set; }
public bool IsClickable
{
get
{
return _isClickable;
}
set
{
_isClickable = value;
foreach (var csc in wpCardCollections.Children.OfType())
csc.IsClickable = value;
foreach (var csc in wpCardCollections2.Children.OfType())
csc.IsClickable = value;
}
}
public bool IsDisplaySorted { get; set; }
public bool IsVPsVisible { get; set; }
public bool DisplayEmptyPile
{
get { return _displayEmptyPile; }
set
{
_displayEmptyPile = value;
if (CardMat == null || PileCount == 0)
UpdateCardDisplay();
}
}
public CardSize CardSize
{
get { return _cardSize; }
set
{
_cardSize = value;
UpdateCardDisplay();
}
}
public CardMat CardMat
{
get { return _cardPile; }
set
{
// Unsubscribe from the old one if necessary
if (_cardPile != null)
_cardPile.CardLabels.CardLabelsChanged -= CardLabels_CardLabelsChanged;
_cardPile = value;
// Subscribe to the new one if necessary
if (_cardPile != null)
_cardPile.CardLabels.CardLabelsChanged += CardLabels_CardLabelsChanged;
UpdateCardDisplay();
}
}
public Dictionary TokenDict
{
private get { return _tokenDict; }
set
{
_tokenDict = value;
UpdateCardDisplay();
}
}
public DominionBase.Piles.Visibility PileVisibility => CardMat?.Visibility ?? DominionBase.Piles.Visibility.All;
private int PileCount => CardMat?.Count ?? 0;
public string PileName
{
get { return _pileName; }
set
{
_pileName = value;
UpdateCardDisplay();
}
}
private void CardLabels_CardLabelsChanged(object sender, CardLabelsChangedEventArgs e)
{
if (Dispatcher.CheckAccess())
{
// Might need to do more than this; for now, let's see if it triggers correctly
UpdateCardDisplay();
}
else
{
Dispatcher.BeginInvoke(new EventHandler(CardLabels_CardLabelsChanged), System.Windows.Threading.DispatcherPriority.Normal, sender, e);
}
}
public void UpdateCardDisplay()
{
wpCardCollections.Children.Clear();
wpCardCollections2.Children.Clear();
wpToolTipCards.Children.Clear();
nPileName.Content = PileName;
if (CardSize == CardSize.Full && CardMat != null && PileCount > 0)
{
foreach (var card in CardMat)
{
var ttd = new ToolTipIDisplayable
{
Card = card,
Margin = new Thickness(3)
};
wpToolTipCards.Children.Add(ttd);
}
}
else
{
nPileName.Visibility = string.IsNullOrEmpty(PileName) ? System.Windows.Visibility.Collapsed : System.Windows.Visibility.Visible;
if (ExactCount && CardMat != null && PileCount > 0)
{
lCount.Visibility = System.Windows.Visibility.Visible;
lCount.Content = $"({StringUtility.Plural("Card", PileCount, true)})";
}
else
{
lCount.Visibility = System.Windows.Visibility.Collapsed;
}
var currentWrapPanel = wpCardCollections;
foreach (var cardStack in GenerateCardStacks())
{
var csc = new Controls.CardStackControl
{
MinWidth = MinStackWidth,
Foreground = Foreground,
CardSize = CardSize,
ExactCount = ExactCount,
IsCardsVisible = IsCardsVisible,
Player = Player,
Phase = Phase,
PlayerMode = PlayerMode,
PileVisibility = PileVisibility,
CardCollection = cardStack,
CardLabels = new CardLabelCollection(CardMat?.CardLabels.Where(cl => cl.AttachedCard == cardStack[0]))
};
if (TokenDict != null && TokenDict.ContainsKey(cardStack[0]))
csc.Tokens = TokenDict[cardStack[0]];
//if (TokenDict != null && TokenDict.ContainsKey(cardStack[0]))
// csc.Tokens = TokenDict[cardStack[0]];
if (IsVPsVisible && cardStack[0].HasVPs)
csc.CountVPs(CardMat);
else
csc.HideVPs();
if (SplitAt != null && csc.CardCollection.Any() && SplitAt(csc.CardCollection.First()))
currentWrapPanel = wpCardCollections2;
currentWrapPanel.Children.Add(csc);
}
}
InvalidateVisual();
}
public IPlayer Player
{
private get { return _player; }
set
{
_player = value;
foreach (var csc in wpCardCollections.Children.OfType())
csc.Player = value;
}
}
public PhaseEnum Phase
{
private get { return _playerPhase; }
set
{
_playerPhase = value;
foreach (var csc in wpCardCollections.Children.OfType())
csc.Phase = value;
}
}
public PlayerMode PlayerMode
{
private get { return _playerMode; }
set
{
_playerMode = value;
foreach (var csc in wpCardCollections.Children.OfType())
csc.PlayerMode = value;
}
}
private IEnumerable GenerateCardStacks()
{
var builtCardStacks = new OrderedDictionary();
var cardStacks = new List();
if (CardMat == null || PileCount == 0)
{
if (DisplayEmptyPile)
cardStacks.Add(new DisplayableCollection() { new DominionBase.Cards.Universal.Blank() });
return cardStacks;
}
int count;
switch (PileVisibility)
{
case DominionBase.Piles.Visibility.All:
IEnumerable copy = new DisplayableCollection(CardMat);
if (IsDisplaySorted)
{
copy = copy.OrderBy(c => c.Facing).ThenBy(c => CardMat.CardLabels.Any(cl => cl.AttachedCard == c)).ThenBy(c => c.Name).ThenByDescending(c => (c as IPoints)?.ComputeVictoryPoints(Player, CardMat.OfType()) ?? 0);
}
foreach (var card in copy)
{
var key1 = IsCardsVisible
? ((card.IsStackable
&& card.Facing == Facing.FaceUp
&& !CardMat.CardLabels.Any(cl => cl.AttachedCard == card))
// The card must be stackable, be face up, and not have any labels attached to it
? card.Type.ToString()
// Otherwise, it gets its own stack
: card.UniqueId.ToString())
// If the cards aren't visible, they all get the same key
: string.Empty;
// Stacks can be different if the vps for cards with the same name are different (e.g. Distant Lands)
var vps = IsCardsVisible && card.IsStackable && card is IPoints cp
? cp.ComputeVictoryPoints(Player, CardMat.OfType())
: 0;
var key = new Tuple(key1, vps);
if (!builtCardStacks.Contains(key))
builtCardStacks[key] = new DisplayableCollection();
((DisplayableCollection)builtCardStacks[key]).AddRange(card.Stack());
}
cardStacks.AddRange(builtCardStacks.Values.OfType());
break;
case DominionBase.Piles.Visibility.Top:
if (PileCount > 0)
{
cardStacks.Add(new DisplayableCollection());
count = PileCount - 1;
if (!ExactCount && count > 1)
{
var variance = Gaussian.NextGaussian();
count += (int)(variance * (count / 4.0));
}
for (var index = 0; index < count; index++)
cardStacks[0].Add(DominionBase.Cards.Universal.Utility.GenerateCardBack(CardBack.Standard));
if (CardMat.FirstOrDefault() is Card card)
cardStacks[0].Add(card);
}
break;
case DominionBase.Piles.Visibility.None:
if (PileCount > 0)
{
cardStacks.Add(new DisplayableCollection());
count = PileCount;
if (!ExactCount && count > 1)
{
var variance = Gaussian.NextGaussian();
count += (int)(variance * (count / 4.0));
}
for (var index = 0; index < count; index++)
cardStacks[0].Add(DominionBase.Cards.Universal.Utility.GenerateCardBack(CardBack.Standard));
}
break;
}
return cardStacks;
}
internal Control FindGameObject(IGameObject gameObject)
{
return wpCardCollections.Children.OfType().Select(csc => csc.FindGameObject(gameObject)).FirstOrDefault(fe => fe != null);
}
}
}