//Rozdział 9.
//Kompozyt

// Grupowanie obiektów graficznych

public class GraphicObject
{
    public virtual string Name { get; set; } = "Grupa";
    public string Color;
    // todo: składowe
}

public class Circle : GraphicObject
{
    public override string Name => "Okrąg";
}
public class Square : GraphicObject
{
    public override string Name => "Kwadrat";
}
 public class GraphicObject
{
    ...
    private Lazy<List<GraphicObject>> children =
        new Lazy<List<GraphicObject>>();
    public List<GraphicObject> Children => children.Value;
}

public class GraphicObject
{
    private void Print(StringBuilder sb, int depth)
    {
        sb.Append(new string('*', depth))
            .Append(string.IsNullOrWhiteSpace(Color) ? string.Empty :
            $"{Color} ")
            .AppendLine($"{Name}");
        foreach (var child in Children)
            child.Print(sb, depth + 1);
    }
    public override string ToString()
    {
        var sb = new StringBuilder();
        Print(sb, 0);
        return sb.ToString();
    }
}
var drawing = new GraphicObject {Name = "Mój rysunek"};
drawing.Children.Add(new Square {Color = "Czerwony"});
drawing.Children.Add(new Circle{Color="Żółty"});
var group = new GraphicObject();
group.Children.Add(new Circle{Color="Niebieski"});
group.Children.Add(new Square{Color="Niebieski"});
drawing.Children.Add(group);
WriteLine(drawing);


//Sieci neuronowe
public class Neuron
{
    public List<Neuron> In, Out;
}

public void ConnectTo(Neuron other)
{
    Out.Add(other);
    other.In.Add(this);
}

public class NeuronLayer : Collection<Neuron>
{
    public NeuronLayer(int count)
    {
        while (count --> 0)
            Add(new Neuron());
    }
}

var neuron1 = new Neuron();
var neuron2 = new Neuron();
var layer1 = new NeuronLayer(3);
var layer2 = new NeuronLayer(4);

neuron1.ConnectTo(neuron2); //już działa :-)

neuron1.ConnectTo(layer1);
layer2.ConnectTo(neuron1);
layer1.ConnectTo(layer2);

public class Neuron : IEnumerable<Neuron>
{
    public List<Neuron> In, Out;
    public IEnumerator<Neuron> GetEnumerator()
    {
        yield return this;
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public static class ExtensionMethods
{
    public static void ConnectTo(
        this IEnumerable<Neuron> self, IEnumerable<Neuron> other)
    {
        if (ReferenceEquals(self, other)) return;
        foreach (var from in self)
            foreach (var to in other)
            {
                from.Out.Add(to);
                to.In.Add(from);
            }
    }
}

//Opakowanie kompozytu
public abstract class Scalar<T> : IEnumerable<T>
where T : Scalar<T>
{
    public IEnumerator<T> GetEnumerator()
    {
        yield return (T) this;
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public class Foo : Scalar<Foo> {}
var foo = new Foo();
foreach (var x in foo)
{
    // zwróci tylko jedną wartość x
    // gdzie x == foo (referencyjnie) :)
}
