//Rozdział 25.
//Wizytator


public abstract class Expression { /* tutaj nic nie ma (jeszcze) */ }
public class DoubleExpression : Expression
{
    private double value;
    public DoubleExpression(double value) { this.value = value; }
}
public class AdditionExpression : Expression
{
    private Expression left, right;
    public AdditionExpression(Expression left, Expression right)
    {
        this.left = left;
        this.right = right;
    }
}

// Nachalny wizytator
public abstract class Expression
{
    // dodanie nowej operacji
    public abstract void Print(StringBuilder sb);
}
public class AdditionExpression : Expression
{
    ...
    public override void Print(StringBuilder sb)
    {
        sb.Append(value: "(");
        left.Print(sb);
        sb.Append(value: "+");
        right.Print(sb);
        sb.Append(value: ")");
    }
}
 var e = new AdditionExpression(
    left: new DoubleExpression(1),
    right: new AdditionExpression(
        left: new DoubleExpression(2),
        right: new DoubleExpression(3)));
var sb = new StringBuilder();
e.Print(sb);
WriteLine(sb); // (1.0 + (2.0 + 3.0))
// Wyświetlacz reflektywny
 abstract class Expression
{
    // tutaj niczego nie ma!
};
 public static class ExpressionPrinter
{
    public static void Print(DoubleExpression e, StringBuilder sb)
    {
        sb.Append(de.Value);
    }

    public static void Print(AdditionExpression ae, StringBuilder sb)
    {
        sb.Append("(");
        Print(ae.Left, sb); //nie skompiluje się!!!
        sb.Append("+");
        Print(ae.Right, sb); //nie skompiluje się!!!
        sb.Append(")");
    }
}
 public static class ExpressionPrinter
{
    public static void Print(Expression e, StringBuilder sb)
    {
        if (e is DoubleExpression de)
        {
            sb.Append(de.Value);
        }
        else if (e is AdditionExpression ae)
       {
            sb.Append("(");
            Print(ae.Left, sb);
            sb.Append("+");
            Print(ae.Right, sb);
            sb.Append(")");
       }
    }
}
 var e = new AdditionExpression(
    left: new DoubleExpression(1),
    right: new AdditionExpression(
        left: new DoubleExpression(2),
        right: new DoubleExpression(3)));
var sb = new StringBuilder();
ExpressionPrinter.Print(e, sb);
WriteLine(sb);

// Funkcyjny wizytator reflektywny
 type Expression =
| Add of Expression * Expression
| Mul of Expression * Expression
...
 let rec process expr =
    match expr with
    | And(lhs,rhs) -> ...
    | Mul(lhs,rhs) -> ...
    ...
//Usprawnienia
 private static DictType actions = new DictType
{
    [typeof(DoubleExpression)] = (e, sb) =>
    {
        var de = (DoubleExpression) e;
        sb.Append(de.Value);
    },
    [typeof(AdditionExpression)] = (e, sb) =>
    {
    var ae = (AdditionExpression) e;
        sb.Append("(");
        Print(ae.Left, sb);
        sb.Append("+");
        Print(ae.Right, sb);
        sb.Append(")");
    }
};
 public static void Print(this Expression e, StringBuilder sb)
{
    actions[e.GetType()](e, sb);
}
// przykładowe użycie:
myExpression.Print(sb)

// Co to jest dysponowanie?

interface IStuff { }
class Foo : IStuff { }
class Bar : IStuff { }

public class Something
{
    static void func(Foo foo) { }
    static void unc(Bar bar) { }
}

Foo foo = new Foo();
func(foo); // to jest w porządku

Stuff stuff = new Foo;
func(stuff); // oops!
 interface Stuff {
    void call();
}
class Foo : Stuff {
    void call() { func(this); }
}
class Bar : Stuff {
    void call() { func(this); }
}

void func(Foo foo) {}
void func(Bar bar) {}
Stuff foo = new Foo;
foo.call();

//Wizytator dynamiczny

public class ExpressionPrinter
{
    public void Print(AdditionExpression ae, StringBuilder sb)
    {
        sb.Append("(");
        Print(ae.Left, sb);
        sb.Append("+");
        Print(ae.Right, sb);
        sb.Append(")");
    }

    public void Print(DoubleExpression de, StringBuilder sb)
    {
        sb.Append(de.Value);
    }
}

public void Print(AdditionExpression ae, StringBuilder sb)
{
    sb.Append("(");
    Print((dynamic)ae.Left, sb); // <-- przyjrzyj się temu uważnie
    sb.Append("+");
    Print((dynamic)ae.Right, sb); // <-- i jeszcze tutaj
    sb.Append(")");
}
var e = ...; // tak jak wcześniej
var ep = new ExpressionPrinter();
var sb = new StringBuilder();
ep.Print((dynamic)e, sb); // <-- zwróćmy uwagę na rzutowanie w tym miejscu
WriteLine(sb);
// Klasyczny wizytator
public abstract class Expression
{
    public abstract void Accept(IExpressionVisitor visitor);
}

public override void Accept(IExpressionVisitor visitor)
{
    visitor.Visit(this);
}

public interface IExpressionVisitor
{
    void Visit(DoubleExpression de);
    void Visit(AdditionExpression ae);
}

public class ExpressionPrinter : IExpressionVisitor
{
    StringBuilder sb = new StringBuilder();
    public void Visit(DoubleExpression de)
    {
        sb.Append(de.Value);
    }
    public void Visit(AdditionExpression ae)
    {
        //poczekaj na to!
    }
    public override string ToString() => sb.ToString();
}

public void Visit(AdditionExpression ae)
{
    sb.Append("(");
    ae.Left.Accept(this);
    sb.Append("+");
    ae.Right.Accept(this);
    sb.Append(")");
}
 var e = new AdditionExpression(
    new DoubleExpression(1),
    new AdditionExpression(
        new DoubleExpression(2),
        new DoubleExpression(3)));
var ep = new ExpressionPrinter();
ep.Visit(e);
WriteLine(ep.ToString()); // (1 + (2 + 3))
 public static class ExtensionMethods
{
    public void Print(this DoubleExpression e, StringBuilder sb)
    {
        var ep = new ExpressionPrinter();
        ep.Print(e, sb);
    }
    // tutaj inne przeciążenia
}
// Implementacja dodatkowego wizytatora
 public class ExpressionCalculator : IExpressionVisitor
{
    public double Result;
    public void Visit(DoubleExpression de)
    {
        Result = de.Value;
    }
    public void Visit(AdditionExpression ae)
    {
        //za chwilę!
    }
    // być może tym, czego naprawdę potrzebujemy, jest dwukrotne wywołanie metody Visit(…)
}
 public void Visit(AdditionExpression ae)
{
    ae.Left.Accept(this);
    var a = Result;
    ae.Right.Accept(this);
    var b = Result;
    Result = a + b;
}
 var calc = new ExpressionCalculator();
calc.Visit(e);
WriteLine($"{ep} = {calc.Result}");
// wyświetla "(1+(2+3)) = 6"
 Wizytator acykliczny
 public interface IVisitor<TVisitable>
{
    void Visit(TVisitable obj);
}
 public interface IVisitor {} //interfejs — znacznik
 public abstract class Expression
{
    public virtual void Accept(IVisitor visitor)
    {
        if (visitor is IVisitor<Expression> typed)
            typed.Visit(this);
    }
}
 public class ExpressionPrinter : IVisitor,
    IVisitor<Expression>,
    IVisitor<DoubleExpression>,
    IVisitor<AdditionExpression>
{
    StringBuilder sb = new StringBuilder();
    public void Visit(DoubleExpression de) { ... }
    public void Visit(AdditionExpression ae) { ... }
    public void Visit(Expression obj)
    {
        // domyślna obsługa?
    }
    public override string ToString() => sb.ToString();
}
 
