using System;
using System.Collections.Generic;
 
namespace DoFactory.GangOfFour.Flyweight.RealWorld
{
  /// 
  /// MainApp startup class for Real-World 
  /// Flyweight Design Pattern.
  /// 
  class MainApp
  {
	/// 
	/// Entry point into console application.
	/// 
    static void Main()
    {
      // Build a document with text
      string document = "AAZZBBZB";
      char[] chars = document.ToCharArray();
 
      CharacterFactory factory = new CharacterFactory();
 
      // extrinsic state
      int pointSize = 10;
 
      // For each character use a flyweight object
      foreach (char c in chars)
      {
        pointSize++;
        Character character = factory.GetCharacter(c);
        character.Display(pointSize);
      }
 
      // Wait for user
      Console.ReadKey();
    }
  }
 
  /// 
  /// The 'FlyweightFactory' class
  /// 
  class CharacterFactory
  {
    private Dictionary _characters = new Dictionary();
 
    public Character GetCharacter(char key)
    {
      // Uses "lazy initialization"
      Character character = null;
      if (_characters.ContainsKey(key))
      {
        character = _characters[key];
      }
      else
      {
        switch (key)
        {
          case 'A': character = new CharacterA(); break;
          case 'B': character = new CharacterB(); break;
          //...

          case 'Z': character = new CharacterZ(); break;
        }
        _characters.Add(key, character);
      }
      return character;
    }
  }
 
  /// 
  /// The 'Flyweight' abstract class
  /// 
  abstract class Character
  {
    protected char symbol;
    protected int width;
    protected int height;
    protected int ascent;
    protected int descent;
    protected int pointSize;
 
    public abstract void Display(int pointSize);
  }
 
  /// 
  /// A 'ConcreteFlyweight' class
  /// 
  class CharacterA : Character
  {
    // Constructor
    public CharacterA()
    {
      this.symbol = 'A';
      this.height = 100;
      this.width = 120;
      this.ascent = 70;
      this.descent = 0;
    }
 
    public override void Display(int pointSize)
    {
      this.pointSize = pointSize;
      Console.WriteLine(this.symbol + " (pointsize " + this.pointSize + ")");
    }
  }
 
  /// 
  /// A 'ConcreteFlyweight' class
  /// 
  class CharacterB : Character
  {
    // Constructor
    public CharacterB()
    {
      this.symbol = 'B';
      this.height = 100;
      this.width = 140;
      this.ascent = 72;
      this.descent = 0;
    }
 
    public override void Display(int pointSize)
    {
      this.pointSize = pointSize;
      Console.WriteLine(this.symbol + " (pointsize " + this.pointSize + ")");
    }
  }
 
  // ... C, D, E, etc.
  /// 
  /// A 'ConcreteFlyweight' class
  /// 
  class CharacterZ : Character
  {
    // Constructor
    public CharacterZ()
    {
      this.symbol = 'Z';
      this.height = 100;
      this.width = 100;
      this.ascent = 68;
      this.descent = 0;
    }
 
    public override void Display(int pointSize)
    {
      this.pointSize = pointSize;
      Console.WriteLine(this.symbol + " (pointsize " + this.pointSize + ")");
    }
  }
}