//Część II
//Wzorce kreacyjne
  
//Rozdział 3.
//Budowniczy


var hello = "witaj";
var sb = new StringBuilder();
sb.Append("<p>");
sb.Append(hello);
sb.Append("</p>");
WriteLine(sb);

var words = new[] { "witaj", "świecie" };
sb.Clear();
sb.Append("<ul>");
foreach (var word in words)
{
    sb.AppendFormat("<li>{0}</li>", word);
}
sb.Append("</ul>");
WriteLine(sb);

class HtmlElement
{
    public string Name, Text;
    public List<HtmlElement> Elements = new List<HtmlElement>();
    private const int indentSize = 2;
    public HtmlElement() {}
    public HtmlElement(string name, string text)
    {
        Name = name;
        Text = text;
    }
}
var words = new[] { "witaj", "świecie" };
var tag = new HtmlElement("ul", null);
foreach (var word in words)
    tag.Elements.Add("li", word);
WriteLine(tag); // wywołania tag.ToString()

//Prosty Budowniczy
class HtmlBuilder
{
    protected readonly string rootName;
    protected HtmlElement root = new HtmlElement();
    public HtmlBuilder(string rootName)
    {
        this.rootName = rootName;
        root.Name = rootName;
    }
    public void AddChild(string childName, string childText)
    {
        var e = new HtmlElement(childName, childText);
        root.Elements.Add(e);
    }
    public override string ToString() => root.ToString();
}

var builder = new HtmlBuilder("ul");
builder.AddChild("li", "witaj");
builder.AddChild("li", "świecie");
WriteLine(builder.ToString());

//Płynny Budowniczy
public HtmlBuilder AddChild(string childName, string childText)
{
    var e = new HtmlElement(childName, childText);
    root.Elements.Add(e);
    return this;
}

var builder = new HtmlBuilder("ul");
builder.AddChild("li", "witaj").AddChild("li", "świecie");
WriteLine(builder.ToString());

//Komunikowanie zamiaru

class HtmlElement
{
    protected string Name, Text;
    protected List<HtmlElement> Elements = new List<HtmlElement>();
    protected const int indentSize = 2;
    //ukryj konstruktory!
    protected HtmlElement() {}
    protected HtmlElement(string name, string text)
    {
        Name = name;
        Text = text;
    }
    //metoda fabryczna
    public static HtmlBuilder Create(string name) => new
    HtmlBuilder(name);
}
var builder = HtmlElement.Create("ul");
builder.AddChild("li", "witaj").AddChild("li", "świecie");
WriteLine(builder);
protected HtmlElement root = new HtmlElement();
public static implicit operator HtmlElement(HtmlBuilder builder)
{
    return builder.root;
}
HtmlElement root = HtmlElement
    .Create("ul")
    .AddChildFluent("li", "witaj")
    .AddChildFluent("li", "świecie");
WriteLine(root);
public HtmlElement Build() => root;

//Złożony Budowniczy
public class Person
{
    //adres
    public string StreetAddress, Postcode, City;
    //informacje o zatrudnieniu
    public string CompanyName, Position;
    public int AnnualIncome;
}
public class PersonBuilder
{
    //obiekt, który budujemy
    protected Person person; //to jest referencja!
    public PersonBuilder() => person = new Person();
    protected PersonBuilder(Person person) => this.person = person;
    public PersonAddressBuilder Lives => new
        PersonAddressBuilder(person);
    public PersonJobBuilder Works => new PersonJobBuilder(person);
    public static implicit operator Person(PersonBuilder pb)
    {
        return pb.person;
    }
}

public class PersonAddressBuilder : PersonBuilder
{
    public PersonAddressBuilder(Person person) : base(person)
    {
        this.person = person;
    }
    public PersonAddressBuilder At(string streetAddress)
    {
        person.StreetAddress = streetAddress;
        return this;
    }
    public PersonAddressBuilder WithPostcode(string postcode)
    {
        person.Postcode = postcode;
        return this;
    }
    public PersonAddressBuilder In(string city)
    {
        person.City = city;
        return this;
    }
};

var pb = new PersonBuilder();
Person person = pb
    .Lives
        .At("Aleja Gdańska 123")
        .In("Gdańsk")
        .WithPostcode("82200")
    .Works
        .At("Fabryka Maszyn")
        .AsA("inżynier")
        .Earning(123000);
WriteLine(person);
// StreetAddress: Aleja Gdańska 123, Postcode: 82200, City: Malbork,
// CompanyName: Fabryka Maszyn, Position: inżynier, AnnualIncome: 123000
 Reference to object that is being built=Referencja do budowanego obiektu

//Parametry Budowniczego
public class Email
{
    public string From, To, Subject, Body;
    //tutaj inne składowe
}
public class EmailBuilder
{
    private readonly Email email;
    public EmailBuilder(Email email) => this.email = email;
    public EmailBuilder From(string from)
    {
        email.From = from;
        return this;
    }
    //tutaj inne płynne składowe
}
public class MailService
{
    public class EmailBuilder { ... }
    private void SendEmailInternal(Email email) {}
    public void SendEmail(Action<EmailBuilder> builder)
    {
        var email = new Email();
        builder(new EmailBuilder(email));
        SendEmailInternal(email);
    }
}

var ms = new MailService();
ms.SendEmail(email => email.From("foo@bar.com")
    .To("bar@baz.com")
    .Body("Cześć, jak się masz?"));
// Dziedziczenie płynnego interfejsu

public class Person
{
    public string Name;
    public string Position;
}

public abstract class PersonBuilder
{
    protected Person person = new Person();
    public Person Build()
    {
        return person;
    }
}

public class PersonInfoBuilder : PersonBuilder
{
    public PersonInfoBuilder Called(string name)
    {
        person.Name = name;
        return this;
    }
}

public class PersonJobBuilder : PersonInfoBuilder
{
    public PersonJobBuilder WorksAsA(string position)
    {
        person.Position = position;
        return this;
    }
}

var me = Person.New
    .Called("Dmitri")
    .WorksAsA("Klasyfikator") // nie skompiluje się
    .Build();

public class PersonInfoBuilder<SELF> : PersonBuilder
where SELF : PersonInfoBuilder<SELF>
{
    public SELF Called(string name)
    {
        person.Name = name;
        return (SELF) this;
    }
}

public class PersonJobBuilder<SELF>
    : PersonInfoBuilder<PersonJobBuilder<SELF>>
    where SELF : PersonJobBuilder<SELF>
{
    public SELF WorksAsA(string position)
    {
        person.Position = position;
        return (SELF) this;
    }
}

PersonInfoBuilder<PersonJobBuilder<PersonBirthDateBuilder<SELF>>>

public class PersonBirthDateBuilder<SELF>
    : PersonJobBuilder<PersonBirthDateBuilder<SELF>>
    where SELF : PersonBirthDateBuilder<SELF>
{
    public SELF Born(DateTime dateOfBirth)
    {
        person.DateOfBirth = dateOfBirth;
        return (SELF)this;
    }
}

public class Person
{
    public class Builder : PersonJobBuilder<Builder>
    {
        internal Builder() {}
    }
    public static Builder New => new Builder();
    //inne składowe pominięto
}

//Konstrukcja DSL w F#

let p args =
    let allArgs = args |> String.concat "\n"
    [" <p>"; allArgs; "</p>"] |> String.concat "\n"
let img url = "<img src=\"" + url + "\"/>"
 let html =
    p[
        "Zobacz to zdjęcie";
        img "pokemon.com/pikachu.png"
     ]
printfn "%s" html
 <p>
Zobacz to zdjęcie
<img src="pokemon.com/pikachu.png"/>
</p>
 
