//Rozdział 10.
//Dekorator

// Niestandardowy StringBuilder

public class CodeBuilder
{
    private StringBuilder builder = new StringBuilder();
    private int indentLevel = 0;
    public CodeBuilder Indent()
    {
        indentLevel++;
        return this;
    }
}

public class CodeBuilder
{
    public StringBuilder Append(string value)
    {
        return builder.Append(value);
    }
    public StringBuilder AppendLine()
    {
        return builder.AppendLine();
    }
    //inne wygenerowane składowe pominięto
}
myBuilder.Append("Witaj").AppendLine(" świecie");

public class CodeBuilder
{
    public CodeBuilder Append(char value, int repeatCount)
    {
        builder.Append(value, repeatCount);
        return this; //zwraca CodeBuilder, a nie StringBuilder
    }
...
}

//Adapter-Dekorator
public static implicit operator CodeBuilder(string s)
{
    var cb = new CodeBuilder();
    cb.sb.Append(s);
    return cb;
}
public static CodeBuilder operator +(CodeBuilder cb, string s)
{
    cb.Append(s);
    return cb;
}
CodeBuilder cb = "witaj ";
cb += "świecie";
WriteLine(cb);

// Wielokrotne dziedziczenie
public class Bird
{
    public void Fly() { ... }
}
public class Lizard
{
    public void Crawl() { ... }
}
public class Dragon : Bird, Lizard {} //tego nie można zrobić!

public interface IBird
{
    void Fly();
}
public interface ILizard
{
    void Crawl();
}

public class Dragon: IBird, ILizard
{
    private readonly IBird bird;
    private readonly ILizard lizard;
    public Dragon(IBird bird, ILizard lizard)
    {
        this.bird = bird;
        this.lizard = lizard;
    }
    public void Crawl()
    {
        lizard.Crawl();
    }
    public void Fly()
    {
        bird.Fly();
    }
}

public interface ICreature
{
    int Age { get; set; }
}

public interface IBird : ICreature
{
    void Fly();
}

public interface ILizard : ICreature
{
    void Crawl();
}

public class Bird : IBird
{
    public int Age { get; set; }
    public void Fly()
    {
        if (Age >= 10)
            WriteLine("Ja latam!");
    }
}

public class Lizard : ILizard
{
    public int Age { get; set; }
    public void Crawl()
    {
        if (Age < 10)
            WriteLine("Ja pełzam!");
    }
}

public class Dragon : IBird, ILizard
{
...
    public int Age { get; set; }
}

public int Age
{
    get => bird.Age;
    set => bird.Age = lizard.Age = value;
}

public Dragon(IBird bird, ILizard lizard)
{
    this.bird = bird;
    this.lizard = lizard;
    bird.Age = lizard.Age;
}

// Dynamiczna kompozycja dekoratora
public abstract class Shape
{
    public virtual string AsString() => string.Empty;
}

public sealed class Circle : Shape
{
    private float radius;
    public Circle() : this(0)
    {
    }
    public Circle(float radius)
    {
        this.radius = radius;
    }
    public void Resize(float factor)
    {
        radius *= factor;
    }
    public override string AsString() => $"Okrąg o promieniu {radius}";
}
//podobną implementację klasy Square ze składową 'side' pominięto
 public class ColoredShape : Shape
{
    private readonly Shape shape;
    private readonly string color;
    public ColoredShape(Shape shape, string color)
    {
        this.shape = shape;
        this.color = color;
    }
    public override string AsString()=> $"{shape.AsString()} ma kolor {color}";
}

public class TransparentShape : Shape
{
    private readonly Shape shape;
    private readonly float transparency;
    public TransparentShape(Shape shape, float transparency)
    {
        this.shape = shape;
        this.transparency = transparency;
    }
    public override string AsString() =>
        $"{shape.AsString()} ma przezroczystość {transparency * 100.0f}%";
}

var circle = new Circle(2);
WriteLine(circle.AsString());
//Okrąg o promieniu 2
var redSquare = new ColoredShape(circle, "red");
WriteLine(redSquare.AsString());
//Okrąg o promieniu 2 ma kolor czerwony
var redHalfTransparentSquare = new TransparentShape(redSquare, 0.5f);
WriteLine(redHalfTransparentSquare.AsString());
// Okrąg o promieniu 2 ma kolor czerwony i  ma przezroczystość 50%
 var redSquare = new ColoredShape(circle, "red");
redCircle.Resize(2); // oops!

//Dekorator statyczny

public class ColoredShape<T> : Shape
    where T : Shape, new()
{
    private readonly string color;
    private readonly T shape = new T();

    public ColoredShape() : this("czarny") {}
    public ColoredShape(string color) { this.color = color; }

    public override string AsString() =>
        return $"{shape.AsString()} ma kolor {color}";
}

ColoredShape<Circle> blueCircle = new
ColoredShape<Circle>("blue");
WriteLine(blueCircle.AsString());
//Okrąg o promieniu 0 ma kolor niebieski

TransparentShape<ColoredShape<Square>> blackHalfSquare = new
TransparentShape<ColoredShape<Square>>(0.4f);
WriteLine(blackHalfSquare.AsString());
//Kwadrat o boku 0 ma kolor czarny i ma przezroczystość 40
 Dekorator funkcyjny
 let doWork() =
    printfn "Wykonuję pewną pracę"
 let logger work name =
    let sw = Stopwatch.StartNew()
        printfn "%s %s" "Wejście do metody" name
        work()
        sw.Stop()
        printfn "Wyjście z metody %s; upłynęło %fs" name sw.Elapsed.TotalSeconds
 let loggedWork() = logger doWork "doWork"
loggedWork()
// Wejście do metody doWork
// Wykonuję pewną pracę
// Wyjście z metody doWork; upłynęło 0,097824 s
 
