//Rozdział 17.
//Iterator

foreach (x in y)
    Console.WriteLine(x);
 var enumerator = ((IEnumerable<Foo>)y).GetEnumerator();
while (enumerator.MoveNext())
{
    temp = enumerator.Current;
    Console.WriteLine(temp);
}
 IEnumerable<int> GetSomeNumbers()
{
    yield return 1;
    yield return 2;
    yield return 3;
}
// Właściwości wspierane przez tablice

public class Creature
{
    public int Strength { get; set; }
    public int Agility { get; set; }
    public int Intelligence { get; set; }
}

public double SumOfStats => Strength + Agility + Intelligence;
public double AverageStat => SumOfStats / 3.0;
public double MaxStat => Math.Max(
    Math.Max(Strength, Agility), Intelligence);
private int [] stats = new int[3];
private const int strength = 0;

public int Strength
{
    get => stats[strength];
    set => stats[strength] = value;
}

//to samo dla innych właściwości
public double AverageStat => stats.Average();
public double SumOfStats => stats.Sum();
public double MaxStat => stats.Max();
public IEnumerable<int> Stats => stats;
public class Creature : IEnumerable<int>
{
    //jak wcześniej

    public IEnumerator<int> GetEnumerator()
        => stats.AsEnumerable().GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator()
        => GetEnumerator();
    public int this[int index]
    {
        get => stats[index];
        set => stats[index] = value;
    }
}

//Stwórzmy iterator

public class Node<T>
{
    public T Value;
    public Node<T> Left, Right;
    public Node<T> Parent;
    public Node(T value)
    {
        Value = value;
    }

    public Node(T value, Node<T> left, Node<T> right)
    {
        Value = value;
        Left = left;
        Right = right;

        left.Parent = right.Parent = this;
    }
}
// 1
// / \
// 2 3
var root = new Node<int>(1,
    new Node<int>(2), new Node<int>(3));
public class InOrderIterator<T>
{
    public Node<T> Current { get; set; }
    private readonly Node<T> root;
    private bool yieldedStart;

    public InOrderIterator(Node<T> root)
    {
        this.root = Current = root;
        while (Current.Left != null)
            Current = Current.Left;
    }

    public bool MoveNext()
    {
        // todo
    }
}

public bool MoveNext()
{
    if (!yieldedStart)
    {
        yieldedStart = true;
        return true;
    }

    if (Current.Right != null)
    {
        Current = Current.Right;
        while (Current.Left != null)
            Current = Current.Left;
        return true;
    }
    else
    {
        var p = Current.Parent;
        while (p != null && Current == p.Right)
        {
            Current = p;
            p = p.Parent;
        }
        Current = p;
        return Current != null;
    }
}

var it = new InOrderIterator<int>(root);
while (it.MoveNext())
{
    Write(it.Current.Value);
    Write(',');
}
WriteLine();
// wyświetla 213

public class BinaryTree<T>
{
    private Node<T> root;
    public BinaryTree(Node<T> root)
    {
        this.root = root;
    }

    public InOrderIterator<T> GetEnumerator()
    {
        return new InOrderIterator<T>(root);
    }
}

var root = new Node<int>(1,
    new Node<int>(2), new Node<int>(3));
var tree = new BinaryTree<int>(root);
foreach (var node in tree)
    WriteLine(node.Value); // 2 1 3

//Ulepszony iterator
public IEnumerable<Node<T>> NaturalInOrder
{
    get
    {
        IEnumerable<Node<T>> TraverseInOrder(Node<T> current)
        {
            if (current.Left != null)
            {
                foreach (var left in TraverseInOrder(current.Left))
                    yield return left;
            }
            yield return current;
            if (current.Right != null)
            {
                foreach (var right in TraverseInOrder(current.Right))
                    yield return right;
            }
        }
        foreach (var node in TraverseInOrder(root))
            yield return node;
    }
}

var root = new Node<int>(1,
    new Node<int>(2), new Node<int>(3));
var tree = new BinaryTree<int>(root);
WriteLine(string.Join(",", tree.NaturalInOrder.Select(x =>
x.Value)));
// 2,1,3
 
