// Zasada pojedynczej odpowiedzialności
public class Journal
{
    private readonly List<string> entries = new List<string>();
    // prosty licznik całkowitej liczby wpisów
    private static int count = 0;
}

public void AddEntry(string text)
{
    entries.Add($"{++count}: {text}");
}

public void RemoveEntry(int index)
{
    entries.RemoveAt(index);
}

var j = new Journal();
j.AddEntry("Płakałem dzisiaj.");
j.AddEntry("Zjadłem robaka.");

public void Save(string filename, bool overwrite = false)
{
    File.WriteAllText(filename, ToString());
}

public class PersistenceManager
{
    public void SaveToFile(Journal journal, string filename,
                           bool overwrite = false)
{
    if (overwrite || !File.Exists(filename))
        File.WriteAllText(filename, journal.ToString());
    }
}
 //Zasada otwarty-zamknięty

public enum Color
{
    Red, Green, Blue
}

public enum Size
{
    Small, Medium, Large, Yuge
}

public class Product
{
    public string Name;
    public Color Color;
    public Size Size;
    public Product(string name, Color color, Size size)
    {
        //tutaj oczywiste rzeczy
    }
}
public class ProductFilter
{
    public IEnumerable<Product> FilterByColor
        (IEnumerable<Product> products, Color color)
    {
        foreach (var p in products)
            if (p.Color == color)
                yield return p;
    }
}
public IEnumerable<Product> FilterBySize
    (IEnumerable<Product> products, Size size)
{
    foreach (var p in products)
        if (p.Size == size)
            yield return p;
}

public IEnumerable<Product> FilterBySizeAndColor(
   IEnumerable<Product> products,
   Size size, Color color)
{
    foreach (var p in products)
        if (p.Size == size && p.Color == color)
            yield return p;
}

public interface ISpecification<T>
{
    bool IsSatisfied(T item);
}

public interface IFilter<T>
{
    IEnumerable<T> Filter(IEnumerable<T> items, ISpecification<T> spec);
}

public class BetterFilter : IFilter<Product>
{
    public IEnumerable<Product> Filter(IEnumerable<Product> items,
                                       ISpecification<Product> spec)
{
    foreach (var i in items)
        if (spec.IsSatisfied(i))
            yield return i;
    }
}

public class ColorSpecification : ISpecification<Product>
{
    private Color color;
    public ColorSpecification(Color color)
    {
        this.color = color;
    }

    public bool IsSatisfied(Product p)
    {
        return p.Color == color;
    }
}
var apple = new Product("Jabłko", Color.Green, Size.Small);
var tree = new Product("Drzewo", Color.Green, Size.Large);
var house = new Product("Dom", Color.Blue, Size.Large);
Product[] products = {apple, tree, house};
var pf = new ProductFilter();
WriteLine("Produkty zielone:");
foreach (var p in pf.FilterByColor(products, Color.Green))
    WriteLine($" - {p.Name} jest zielony");


public class AndSpecification<T> : ISpecification<T>
{
    private readonly ISpecification<T> first, second;
    public AndSpecification(ISpecification<T> first,
    ISpecification<T> second)
    {
        this.first = first;
        this.second = second;
    }
    public override bool IsSatisfied(T t)
    {
        return first.IsSatisfied(t) && second.IsSatisfied(t);
    }
}
foreach (var p in bf.Filter(products,
    new AndSpecification<Product>(
        new ColorSpecification(Color.Green),
        new SizeSpecification(Size.Large))))
{
    WriteLine($"{p.Name} jest duży");
}
// Drzewo jest duże i zielone
 public abstract class ISpecification<T>
{
    public abstract bool IsSatisfied(T p);
    public static ISpecification<T> operator &(
        ISpecification<T> first, ISpecification<T> second)
    {
        return new AndSpecification<T>(first, second);
    }
}

var largeGreenSpec = new ColorSpecification(Color.Green)
                   & new SizeSpecification(Size.Large);
public static class CriteriaExtensions
{
    public static AndSpecification<Product> And(this Color color,
    Size size)
    {
        return new AndSpecification<Product>(
            new ColorSpecification(color),
            new SizeSpecification(size));
    }
}
 var largeGreenSpec = Color.Green.And(Size.Large);
//Zasada podstawiania Liskov
public class Rectangle
{
    public int Width { get; set; }
    public int Height { get; set; }

    public Rectangle() {}
    public Rectangle(int width, int height)
    {
        Width = width;
        Height = height;
    }
    public int Area => Width * Height;
}

public class Square : Rectangle
{
    public Square(int side)
    {
        Width = Height = side;
    }

    public new int Width
    {
        set { base.Width = base.Height = value; }
    }

    public new int Height
    {
        set { base.Width = base.Height = value; }
    }
}
public static void UseIt(Rectangle r)
{
    r.Height = 10;
    WriteLine($"Oczekiwane pole powierzchni {10*r.Width}, uzyskano {r.Area}");
}
 var rc = new Rectangle(2,3);
UseIt(rc);
// Oczekiwane pole powierzchni 20, uzyskano 20
 var sq = new Square(5);
UseIt(sq);
// Oczekiwane pole powierzchni 50, uzyskano 100
 public bool IsSquare => Width == Height;
//Zasada segregacji interfejsów
public class MyFavoritePrinter /* : IMachine */
{
    void Print(Document d) {}
    void Fax(Document d) {}
    void Scan(Document d) {}
};
 public interface IMachine
{
    void Print(Document d);
    void Fax(Document d);
    void Scan(Document d);
}
 public class OldFashionedPrinter : IMachine
{
    public void Print(Document d)
    {
        //to jest OK, możemy coś tutaj zrobić
    }
    public void Fax(Document d)
    {
        throw new System.NotImplementedException();
    }
    public void Scan(Document d)
    {
        //celowo pozostawione puste

    }
}
[Obsolete("Nieobsługiwane", true)]
public void Scan(Document d)
{
    throw new System.NotImplementedException();
}
 public interface IPrinter
{
    void Print(Document d);
}

public interface IScanner
{
    void Scan(Document d);
}
 public class Printer : IPrinter
{
    public void Print(Document d)
    {
        //tutaj implementacja
    }
}
 public class Photocopier : IPrinter, IScanner
{
    public void Print(Document d) { ... }
    public void Scan(Document d) { ... }
}
 public interface IMultiFunctionDevice : IPrinter, IScanner // IFax etc.
{
    // Tutaj nie ma niczego
}
 public class MultiFunctionMachine : IMultiFunctionDevice
{
    //skomponuj to z kilku modułów
    private IPrinter printer;
    private IScanner scanner;
    public MultiFunctionMachine(IPrinter printer, IScanner scanner)
    {
        this.printer = printer;
        this.scanner = scanner;
    }
    public void Print(Document d)
    {
        printer.Print(d);
    }
    public void Scan(Document d)
    {
        scanner.Scan(d);
    }
}
 asada odwracania zależności
 public enum Relationship
{
    Parent,
    Child,
    Sibling
}
public class Person
{
    public string Name;
       // Tutaj data urodzenia i inne przydatne właściwości
    }
 public class Relationships //niski poziom
{
    public List<(Person,Relationship,Person)> relations 
       = new List<(Person, Relationship, Person)>();
    public void AddParentAndChild(Person parent, Person child)
    {
        relations.Add((parent, Relationship.Parent, child));
        relations.Add((child, Relationship.Child, parent));
    }
}
 public class Research
{
    public Research(Relationships relationships)
    {
        //wysoki poziom: znajdź wszystkie dzieci Jana
        var relations = relationships.Relations;
        foreach (var r in relations
            .Where(x => x.Item1.Name == "Jan"
                        && x.Item2 == Relationship.Parent))
        {
            WriteLine($"Jan ma dziecko o imieniu {r.Item3.Name}");
        }
    }
}
 public interface IRelationshipBrowser
{
    IEnumerable<Person> FindAllChildrenOf(string name);
}
 public class Relationships : IRelationshipBrowser //niski poziom

{
    //te metody nie są już publiczne!
    private List<(Person,Relationship,Person)> relations
        = new List<(Person, Relationship, Person)>();
public IEnumerable<Person> FindAllChildrenOf(string name)
{
    return relations
        .Where(x => x.Item1.Name == name
                 && x.Item2 == Relationship.Parent)
        .Select(r => r.Item3);
    }
}
 public Research(IRelationshipBrowser browser)
{
    foreach (var p in browser.FindAllChildrenOf("John"))
    {
        WriteLine($"Jan ma dziecko imieniem {p.Name}");
    }
}
 
