using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Text; using System.Xml; using DominionBase.Cards; using DominionBase.Players; using DominionBase.Utilities; using DominionBase.Interfaces; using DominionBase.Enums; namespace DominionBase.Piles { public class BoonSupply : IBoonSupply, IComparable, IDisposable, IShuffleablePile { private readonly object _myLock = new object(); private ItemCollection _items = new ItemCollection(); private readonly ItemCollection _discard = new ItemCollection(); protected bool AsynchronousChanging { get; private set; } public event PileChangedEventHandler PileChanged; protected PileChangedEventArgs AsynchronousPileChangedEventArgs { get; private set; } public virtual event TokensChangedEventHandler TokensChanged; public virtual event ShufflingEventHandler Shuffling; public virtual event ShuffledEventHandler Shuffled; protected IGame _Game { get; } public Guid UniqueId { get; } = Guid.NewGuid(); public int Count => _items.Count; #region ICardBase variables, properties, & methods public Cost BaseCost { get; } = new Cost(); public Type BaseType => GetType(); public Categories Category => Categories.Boon; public bool HasCost => false; public bool HasVPs => false; public string SetupText { get; } = string.Empty; public Source Source => Source.Nocturne; public string Text => string.Empty;// _CardBase.Text; public virtual List GetSerializingTypes() { return new List(); } #endregion #region IDisplayable public CardBack CardBack { get { if (_items.Any()) return _items.First().CardBack; return CardBack.Blank; } } public IDisplayable DisplayCard => RootCard; public Edition Edition => Edition.All; public virtual Facing Facing { get; private set; } = Facing.FaceUp; public virtual bool IsStackable => false; public Location Location => Location.Special; public Orientation Orientation { get; } = Orientation.Landscape; public IDisplayable RootCard => Randomizer; public Traits Traits => Traits.None; #endregion #region ITableable public void AddToken(Token token) { Tokens.Add(token); if (TokensChanged != null) { var etcea = new TokensChangedEventArgs(token); TokensChanged(this, etcea); } var pcea = new PileChangedEventArgs(Operation.Refresh); if (AsynchronousChanging) AsynchronousPileChangedEventArgs = pcea; else PileChanged?.Invoke(this, pcea); } public virtual void BeginChanges() { AsynchronousChanging = true; AsynchronousPileChangedEventArgs = null; } public bool CanUndo => false; public virtual void EndChanges() { AsynchronousChanging = false; if (AsynchronousPileChangedEventArgs != null) PileChanged?.Invoke(this, AsynchronousPileChangedEventArgs); AsynchronousPileChangedEventArgs = null; } public IDisplayable Randomizer => null; public Token RemoveToken(Type tokenType) { var foundToken = Tokens.FirstOrDefault(t => t.GetType() == tokenType); return foundToken != null ? RemoveToken(foundToken) : null; } public Token RemoveToken(Token token) { Tokens.Remove(token); if (TokensChanged != null) { var etcea = new TokensChangedEventArgs(token); TokensChanged(this, etcea); } var pcea = new PileChangedEventArgs(Operation.Refresh); if (AsynchronousChanging) AsynchronousPileChangedEventArgs = pcea; else PileChanged?.Invoke(this, pcea); return token; } public Type TableableType => Type; public TokenCollection Tokens { get; } = new TokenCollection(); public Type Type => GetType(); public IEnumerable Types { get { return _items.Select(i => i.GetType()).Union(_discard.Select(i => i.GetType())).Distinct(); } } #endregion #region IShuffleablePile public int DrawCount => _items.Count; public IDisplayable TopDiscard { get { return _discard.FirstOrDefault(); } } #endregion internal BoonSupply(IGame game) { _Game = game; } #region IDisposable variables, properties, & methods // Track whether Dispose has been called. private bool _disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!_disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if (disposing) { // Dispose managed resources. lock (_myLock) _items = null; } // Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed. // Note disposing has been done. _disposed = true; } } ~BoonSupply() { Dispose(false); } #endregion public void Clear() { lock (_myLock) { _items.Clear(); _discard.Clear(); } #if DEBUG TestFireAllEvents(); #endif } public virtual void CheckSetup(Preset preset, Table table) { } public virtual void CheckSetup(Preset preset, IRandomizable card) { } public virtual void End(IPlayer player, DisplayableCollection collection) { } public void AddPlayer(IPlayer player) { //player.PhaseChanged += player_PhaseChangedEvent; //player.PlayerModeChanged += player_PlayerModeChangedEvent; } public void RemovePlayer(IPlayer player) { //player.PhaseChanged -= player_PhaseChangedEvent; //player.PlayerModeChanged -= player_PlayerModeChangedEvent; } //public override void TearDown(IGame game) //{ // base.TearDown(game); // Tokens.TearDown(game); // _CardBase.TearDown(game); // foreach (var player in _Game.Players) // RemovePlayer(player); //} //public override void EndChanges() //{ // AsynchronousChanging = false; // if (AsynchronousPileChangedEventArgs != null) // { // PileChanged?.Invoke(this, AsynchronousPileChangedEventArgs); // } // AsynchronousPileChangedEventArgs = null; //} // private void player_PhaseChangedEvent(object sender, PhaseChangedEventArgs e) //{ // _CardBase?.PhaseChanged(sender, e); // PhaseChanged(sender, e); //} // private void player_PlayerModeChangedEvent(object sender, PlayerModeChangedEventArgs e) //{ // _CardBase?.PlayerModeChanged(sender, e); // PlayerModeChanged(sender, e); //} public virtual DisplayableCollection Stack() { return new DisplayableCollection { this }; } internal void AddTo(IEnumerable items) { foreach (var item in items) AddTo(item); } internal void AddTo(IBoon item) { if (!item.Category.HasFlag(Categories.Boon)) throw new Exception("Cannot add card of a different type to Supply pile"); if (_items.Any(b => b == item) || _discard.Any(b => b == item)) throw new Exception($"Found duplicate Boon {item}"); lock (_myLock) _discard.Add(item); //item.AddedToSupply(_Game, this); var pileChangedCopy = PileChanged; pileChangedCopy?.Invoke(this, new PileChangedEventArgs(null, Operation.Added, new ItemCollection() { item })); } public bool TurnOverNext(IPlayer player) { if (!_items.Any() && _discard.Any()) Shuffle(player); if (!_items.Any()) return false; var next = _items.PopAt(0); _discard.Insert(0, next); if (AsynchronousChanging) { if (AsynchronousPileChangedEventArgs == null) AsynchronousPileChangedEventArgs = new PileChangedEventArgs(null, Operation.Refresh); } else if (PileChanged != null) { var pcea = new PileChangedEventArgs(null, Operation.Refresh); PileChanged(this, pcea); } return true; } public void TurnOverNextAndReceive(IPlayer player) { if (TurnOverNext(player)) ((IBoon)TopDiscard).Receive(player); } public IBoon TakeBoon(IPlayer player = null) { TurnOverNext(player); var boon = (IBoon)TopDiscard; KeepBoon(player, boon); return boon; } public bool KeepBoon(IPlayer player, IBoon boon) { if (TopDiscard != boon) return false; if (!_discard.Any()) throw new Exception("Nothing to take!"); var item = _discard.PopAt(0); if (_Game.State != GameState.Setup) { if (AsynchronousChanging) { if (AsynchronousPileChangedEventArgs == null) AsynchronousPileChangedEventArgs = new PileChangedEventArgs(null, Operation.Removed, item); else AsynchronousPileChangedEventArgs.RemovedCards.Add(item); } else if (PileChanged != null) { var pcea = new PileChangedEventArgs(null, Operation.Removed, item); PileChanged(this, pcea); } } return true; } public void ReturnBoon(IBoon boon) { Contract.Requires(boon != null, "boon cannot be null"); AddTo(boon); } public void Shuffle(IPlayer player = null) { lock (_myLock) { _items.AddRange(_discard); _discard.Clear(); Shuffling?.Invoke(this, new ShuffleEventArgs(player, new ItemCollection(_items))); _items.Shuffle(); var sea = new ShuffleEventArgs(player, new ItemCollection(_items)); Shuffled?.Invoke(this, sea); _items.Clear(); _items.AddRange(sea.ShuffleCards.OfType()); if (AsynchronousChanging) { if (AsynchronousPileChangedEventArgs == null) AsynchronousPileChangedEventArgs = new PileChangedEventArgs(null, Operation.Refresh); } else if (PileChanged != null) { var pcea = new PileChangedEventArgs(null, Operation.Refresh); PileChanged(this, pcea); } } } public void Reset() { var pcea = new PileChangedEventArgs(Operation.Refresh); if (AsynchronousChanging) AsynchronousPileChangedEventArgs = pcea; else PileChanged?.Invoke(this, pcea); } public override string ToString() { return Name; } public int CompareTo(BoonSupply obj) { //return _CardBase.CompareTo(obj._CardBase); return 0; } public int CompareTo(IDisplayable obj) { Contract.Requires(obj != default(IDisplayable), "obj cannot be null"); if (ReferenceEquals(this, obj)) return 0; if (obj is BoonSupply oBoons) return CompareTo(oBoons); if (obj is Card || obj is ISupply || obj is Event || obj is Project || obj is Landmark || obj is State || obj is IBoon || obj is IHex || obj is Artifact) return 1; var c = string.Compare(Name, obj.Name, StringComparison.CurrentCulture); if (c != 0) return c; return UniqueId.CompareTo(obj.UniqueId); } public int CompareTo(ITableable obj) { Contract.Requires(obj != default(ITableable), "obj cannot be null"); if (obj is IDisplayable oDisp) return CompareTo(oDisp); return string.Compare(Name, obj.Name, StringComparison.CurrentCulture); } public string Name => "Boons";// _CardBase.Name; public string ImageName => Type.Name; public void FullSetup() { Setup(); SnapshotSetup(); FinalizeSetup(); } public virtual void Setup() { foreach (var item in _items) item.Init(_Game, _Game.Players); foreach (var item in _items) item.Setup(); } public virtual void SnapshotSetup() { foreach (var item in _items) item.SnapshotSetup(); } public virtual void FinalizeSetup() { foreach (var item in _items) item.FinalizeSetup(); } public virtual void TearDown(IGame game) { foreach (var item in _items) item.TearDown(game); } public XmlNode GenerateXml(XmlDocument doc) { Contract.Requires(doc != null, "doc cannot be null"); var xeSupply = doc.CreateElement("supply"); //var xe = doc.CreateElement("type"); //xe.InnerText = _CardClassType.ToString(); //xeSupply.AppendChild(xe); //xeSupply.AppendChild(LookThrough(c => true).GenerateXml(doc, "cards")); //var xeTypes = doc.CreateElement("types"); //xeSupply.AppendChild(xeTypes); //foreach (var type in _Types) //{ // var xeType = doc.CreateElement("type"); // xeType.InnerText = type.ToString(); // xeTypes.AppendChild(xeType); //} return xeSupply; } public static ISupply Load(Game game, XmlNode xnSupply) { Contract.Requires(xnSupply != null, "xnSupply cannot be null"); var xnType = xnSupply.SelectSingleNode("type"); if (xnType == null) return null; var type = Type.GetType(xnType.InnerText); //Visibility visibility = Visibility.Top; //XmlNode xnVisibility = xnSupply.SelectSingleNode("visibility"); //if (xnVisibility != null) // visibility = (Visibility)Enum.Parse(typeof(Visibility), xnVisibility.InnerText, true); var supply = new Supply(game, null, type); supply.Load(xnSupply); return supply; } public void Load(XmlNode xnSupply) { // This needs to be done in reverse order, as AddTo adds cards one at a time on top of the pile instead of the bottom //foreach (var xnCard in xnSupply.SelectNodes("cards/card").Cast().Reverse()) //{ // var type = Type.GetType(xnCard.Attributes["type"].Value); // AddTo(Card.CreateInstance(type)); //} //var xnSSS = xnSupply.SelectSingleNode("starting_stack_size"); //if (xnSSS != null) // StartingStackSize = int.Parse(xnSSS.InnerText); //foreach (XmlNode xnType in xnSupply.SelectNodes("types/type")) //{ // var type = Type.GetType(xnType.InnerText); // _Types.Add(type); //} //Tokens.AddRange(TokenCollection.Load(xnSupply.SelectSingleNode("tokens"))); } public virtual void TestFireAllEvents() { PileChanged?.Invoke(this, new PileChangedEventArgs(Operation.Refresh)); } } }