using System;
using System.Collections.Generic;
 
namespace DoFactory.GangOfFour.Observer.RealWorld
{
	/// 
	/// MainApp startup class for Real-World 
	/// Observer Design Pattern.
	/// 
  class MainApp
  {
	/// 
	/// Entry point into console application.
	/// 
    static void Main()
    {
      // Create IBM stock and attach investors
      IBM ibm = new IBM("IBM", 120.00);
      ibm.Attach(new Investor("Sorros"));
      ibm.Attach(new Investor("Berkshire"));
 
      // Fluctuating prices will notify investors
      ibm.Price = 120.10;
      ibm.Price = 121.00;
      ibm.Price = 120.50;
      ibm.Price = 120.75;
 
      // Wait for user
      Console.ReadKey();
    }
  }
 
	/// 
	/// The 'Subject' abstract class
	/// 
  abstract class Stock
  {
    private string _symbol;
    private double _price;
    private List _investors = new List();
 
    // Constructor
    public Stock(string symbol, double price)
    {
      this._symbol = symbol;
      this._price = price;
    }
 
    public void Attach(IInvestor investor)
    {
      _investors.Add(investor);
    }
 
    public void Detach(IInvestor investor)
    {
      _investors.Remove(investor);
    }
 
    public void Notify()
    {
      foreach (IInvestor investor in _investors)
      {
        investor.Update(this);
      }
 
      Console.WriteLine("");
    }
 
    // Gets or sets the price

    public double Price
    {
      get { return _price; }
      set
      {
        if (_price != value)
        {
          _price = value;
          Notify();
        }
      }
    }
 
    // Gets the symbol
    public string Symbol
    {
      get { return _symbol; }
    }
  }
 
	/// 
	/// The 'ConcreteSubject' class
	/// 
  class IBM : Stock
  {
    // Constructor
    public IBM(string symbol, double price) : base(symbol, price)
    {
    }
  }
 
	/// 
	/// The 'Observer' interface
	/// 
  interface IInvestor
  {
    void Update(Stock stock);
  }
 
	/// 
	/// The 'ConcreteObserver' class
	/// 
  class Investor : IInvestor
  {
    private string _name;
    private Stock _stock;
 
    // Constructor
    public Investor(string name)
    {
      this._name = name;
    }
 
    public void Update(Stock stock)
    {
      Console.WriteLine("Notified {0} of {1}'s " + "change to {2:C}", 
			_name, stock.Symbol, stock.Price);
    }
 
    // Gets or sets the stock
    public Stock Stock
    {
      get { return _stock; }
      set { _stock = value; }
    }
  }
}