//Rozdział 6.

//Singleton

//Singleton według konwencji

public class Database
{
    /// <summary>
    ///Nie twórz więcej niż jednego egzemplarza.
    /// </summary>
    public Database() {}
};

public static class Globals
{
    public static Database Database = new Database();
}

//Klasyczna implementacja
public class Database
{
    private static int instanceCount = 0;
    Database()
    {
        if (++instanceCount > 1)
            throw new InvalidOperationExeption("Nie można stworzyć >1 bazy danych!");
    }
};

public class Database
{
    private Database() { ... }
    public static Database Instance { get; } = new Database();
}

//Leniwe ładowanie

public class MyDatabase
{
    private MyDatabase()
    {
        Console.WriteLine("Inicjalizacja bazy danych");
    }
    private static Lazy<MyDatabase> instance =
        new Lazy<MyDatabase>(() => new MyDatabase());
        public static MyDatabase Instance => instance.Value;
}

//Kłopoty z Singletonem
public interface IDatabase
{
    int GetPopulation(string name);
}

public class SingletonDatabase : IDatabase
{
    private Dictionary<string, int> capitals;
    private static int instanceCount;
    public static int Count => instanceCount;
    private SingletonDatabase()
    {
        WriteLine("Inicjalizacja bazy danych");
        capitals = File.ReadAllLines(
        Path.Combine(
            new FileInfo(typeof(IDatabase).Assembly.Location).
            DirectoryName,
            "capitals.txt")
        )
        .Batch(2) // z MoreLINQ
        .ToDictionary(
             list => list.ElementAt(0).Trim(),
             list => int.Parse(list.ElementAt(1)));
    }
    public int GetPopulation(string name)
    {
        return capitals[name];
    }
    private static Lazy<SingletonDatabase> instance =
        new Lazy<SingletonDatabase>(() =>
        {
            instanceCount++;
            return new SingletonDatabase();
        });
    public static IDatabase Instance => instance.Value;
}

public class SingletonRecordFinder
{
    public int TotalPopulation(IEnumerable<string> names)
    {
        int result = 0;
        foreach (var name in names)
            result += SingletonDatabase.Instance.GetPopulation(name);
        return result;
    }
}

[Test]
public void SingletonTotalPopulationTest()
{
    //testowanie na faktycznej bazie danych
    var rf = new SingletonRecordFinder();
    var names = new[] {"Seul", "Meksyk"};
    int tp = rf.TotalPopulation(names);
    Assert.That(tp, Is.EqualTo(17500000 + 17400000));
}

public class ConfigurableRecordFinder
{
    private IDatabase database;
    public ConfigurableRecordFinder(IDatabase database)
    {
        this.database = database;
    }
    public int GetTotalPopulation(IEnumerable<string> names)
    {
        int result = 0;
        foreach (var name in names)
            result += database.GetPopulation(name);
        return result;
    }
}

public class DummyDatabase : IDatabase
{
    public int GetPopulation(string name)
    {
        return new Dictionary<string, int>
        {
            ["alpha"] = 1,
            ["beta"] = 2,
            ["gamma"] = 3
        }[name];
    }
}

[Test]
public void DependentTotalPopulationTest()
{
    var db = new DummyDatabase();
    var rf = new ConfigurableRecordFinder(db);
    Assert.That(
        rf.GetTotalPopulation(new[]{"alpha", "gamma"}),
        Is.EqualTo(4));
}

//Singletony a IoC
var builder = new ContainerBuilder();
builder.RegisterType<Database>().SingleInstance(); // <-- singleton!
builder.RegisterType<RecordFinder>();
var container = builder.Build();
var finder = container.Resolve<RecordFinder>();
var finder2 = container.Resolve<RecordFinder>();
WriteLine(ReferenceEquals(finder, finder2)); // True
// finder i finder2 odnoszą się do tej samej bazy danych

//Monostat
public class ChiefExecutiveOfficer
{
    private static string name;
    private static int age;
    public string Name
    {
        get => name;
        set => name = value;
    }
    public int Age
    {
        get => age;
        set => age = value;
    }
}
 
