using System;
using System.Collections.Generic;
using System.Threading;
 
namespace DoFactory.GangOfFour.Singleton.RealWorld
{
  /// 
  /// MainApp startup class for Real-World 
  /// Singleton Design Pattern.
  /// 

  class MainApp
  {
    /// 
	/// Entry point into console application.
	/// 
	static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();
 
      // Same instance?
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }
 
      // Load balance 15 server requests
      LoadBalancer balancer = LoadBalancer.GetLoadBalancer();
      for (int i = 0; i < 15; i++)
      {
        string server = balancer.Server;
        Console.WriteLine("Dispatch Request to: " + server);
      }
 
      // Wait for user
      Console.ReadKey();
    }
  }
 
  /// 
  /// The 'Singleton' class
  /// 

  class LoadBalancer
  {
    private static LoadBalancer _instance;
    private List _servers = new List();
    private Random _random = new Random();
 
    // Lock synchronization object
    private static object syncLock = new object();
 
    // Constructor (protected)
    protected LoadBalancer()
    {
      // List of available servers
      _servers.Add("ServerI");
      _servers.Add("ServerII");
      _servers.Add("ServerIII");
      _servers.Add("ServerIV");
      _servers.Add("ServerV");
    }
 
    public static LoadBalancer GetLoadBalancer()
    {
	// Support multithreaded applications through
	// 'Double checked locking' pattern which (once
	// the instance exists) avoids locking each
	// time the method is invoked

      if (_instance == null)
      {
        lock (syncLock)
        {
          if (_instance == null)
          {
            _instance = new LoadBalancer();
          }
        }
      }
      return _instance;
    }
 
    // Simple, but effective random load balancer
    public string Server
    {
      get
      {
        int r = _random.Next(_servers.Count);
        return _servers[r].ToString();
      }
    }
  }
}