using Biblioteka;

using static System.Console;

Osoba henryk = new() { Nazwisko = "Henryk" };
Osoba maria = new() { Nazwisko = "Maria" };
Osoba julia = new() { Nazwisko = "Julia" };

// Implementowanie funkcji za pomocą metod i operatorów

// wywołanie metody obiektu
Osoba dziecko1 = maria.ProkreacjaZ(henryk);
dziecko1.Nazwisko = "Grzegorz";

// wywołanie metody statycznej
Osoba dziecko2 = Osoba.Prokreacja(henryk, julia);

// wywołanie operatora
Osoba dziecko3 = henryk * maria;

WriteLine($"{henryk.Nazwisko} ma {henryk.Dzieci.Count} dzieci.");
WriteLine($"{maria.Nazwisko} ma {maria.Dzieci.Count} dzieci.");
WriteLine($"{julia.Nazwisko} ma {julia.Dzieci.Count} dzieci.");

WriteLine(
  format: "Pierwsze dziecko {0} nazywa się \"{1}\".",
  arg0: henryk.Nazwisko,
  arg1: henryk.Dzieci[0].Nazwisko);

// Implementowanie działan z wykorzystaniem funkcji lokalnych

WriteLine($"5! is {Osoba.Silnia(5)}");

// Wywoływanie i obsługa zdarzeń

henryk.Krzycz += Henryk_Krzycz;

henryk.Szturchnij();
henryk.Szturchnij();
henryk.Szturchnij();
henryk.Szturchnij();

// praca z typami nieganerycznymi
System.Collections.Hashtable wyszukiwanieObiektow = new();

wyszukiwanieObiektow.Add(key: 1, value: "Alpha");
wyszukiwanieObiektow.Add(key: 2, value: "Beta");
wyszukiwanieObiektow.Add(key: 3, value: "Gamma");
wyszukiwanieObiektow.Add(key: henryk, value: "Delta");

int klucz = 2; // szukamy wartości i kluczu 2
WriteLine(format: "Klucz {0} ma wartość: {1}",
  arg0: klucz,
  arg1: wyszukiwanieObiektow[klucz]);

// szukamy wartości o kluczu równym zmiennej henryk
WriteLine(format: "Klucz {0} ma wartość: {1}",
  arg0: henryk,
  arg1: wyszukiwanieObiektow[henryk]);

// praca z typami generycznymi
Dictionary<int, string> slowanikIntString = new();

slowanikIntString.Add(key: 1, value: "Alpha");
slowanikIntString.Add(key: 2, value: "Beta");
slowanikIntString.Add(key: 3, value: "Gamma");
slowanikIntString.Add(key: 4, value: "Delta");

klucz = 3;
WriteLine(format: "Klucz {0} ma wartość: {1}",
  arg0: klucz,
  arg1: slowanikIntString[klucz]);

// Porównywanie obiektów podczas sortowania

Osoba?[] osoby =
{
  new() { Nazwisko = "Sianiak" },
  null,
  new() { Nazwisko = "Janiak" },
  new() { Nazwisko = "Adun" },
  new() { Nazwisko = null },
  new() { Nazwisko = "Rykszak" }
};

WriteLine("Początkowa lista osób:");
foreach (Osoba? osoba in osoby)
{
  WriteLine($"  {(osoba is null ? "<null> Osoba" : osoba.Nazwisko ?? "<null> Nazwisko")}");
}

WriteLine("Do posortowania użyto implementacji interfejsu IComparable w klasie Osoba:");
Array.Sort(osoby);
foreach (Osoba? osoba in osoby)
{
  WriteLine($"  {(osoba is null ? "<null> Osoba" : osoba.Nazwisko ?? "<null> Nazwisko")}");
}

// Porównywanie obiektów za pomocą osobnej klasy

WriteLine("Do posortowania użyto implementacji interfejsu IComparer w klasie OsobaComparer:");
Array.Sort(osoby, new OsobaComparer());
foreach (Osoba? osoba in osoby)
{
  WriteLine($"  {(osoba is null ? "<null> Osoba" : osoba.Nazwisko ?? "<null> Nazwisko")}");
}

// Przechowywanie w pamięci typów referencyjnych i typów wartości

int liczba1 = 49;
long liczba2 = 12;
System.Drawing.Point location = new(x: 4, y: 5);
Osoba kewin = new() { Nazwisko = "Kewin", 
  DataUrodzenia = new(year: 1988, month: 9, day: 23) };
Osoba staszek;

// praca ze strukturami

WektorPrzesuniecia wp1 = new(3, 5);
WektorPrzesuniecia wp2 = new(-2, 7);
WektorPrzesuniecia wp3 = wp1 + wp2;

WriteLine($"({wp1.X}, {wp1.Y}) + ({wp2.X}, {wp2.Y}) = ({wp3.X}, {wp3.Y})");

// dziedziczenie klas

Pracownik jacek = new()
{
  Nazwisko = "Jacek Jankowski",
  DataUrodzenia = new(year: 1990, month: 7, day: 28)
};
jacek.WypiszWKonsoli();

// rozbudowywanie klas

jacek.KodPracownika = "JJ001";
jacek.DataZatrudnienia = new(year: 2014, month: 11, day: 23);
WriteLine($"{jacek.Nazwisko} został zatrudniony {jacek.DataZatrudnienia:dd/MM/yy}");

// pokrywanie elementów klasy

WriteLine(jacek.ToString());

// polimorfizm

Pracownik alicjaJakoPracownik = new()
  { Nazwisko = "Alicja", KodPracownika = "AA123" };

Osoba alicjaJakoOsoba = alicjaJakoPracownik;
alicjaJakoPracownik.WypiszWKonsoli();
alicjaJakoOsoba.WypiszWKonsoli();
WriteLine(alicjaJakoPracownik.ToString());
WriteLine(alicjaJakoOsoba.ToString());

// jawne rzutowanie

if (alicjaJakoOsoba is Pracownik alicjaJawnie)
{
  WriteLine($"{nameof(alicjaJakoOsoba)} JEST pracownikiem");
  // Pracownik alicjaJawnie = (Pracownik)alicjaJakoOsoba;
  // bezpieczna praca z alicjaJawnie
}

Pracownik? alicjaPracownik = alicjaJakoOsoba as Pracownik; // może być null

if (alicjaPracownik is not null)
{
  WriteLine($"{nameof(alicjaJakoOsoba)} JAKO pracownik");
  // pracuj z obiektem alicjaPracownik
}

// Dziedziczenie wyjątków

try
{
  jacek.PodrozWCzasie(kiedy: new(1999, 12, 31));
  jacek.PodrozWCzasie(kiedy: new(1950, 12, 25));
}
catch (WyjatekOsoba ex)
{
  WriteLine(ex.Message);
}

// Używanie metod statycznych do ponownego wykorzystania funkcji

string email1 = "pamela@test.com";
string email2 = "jan&test.com";

WriteLine("{0} to poprawny adres e-mail: {1}",
  arg0: email1,
  arg1: RozszerzeniaDlaString.AdresPoprawny(email1));

WriteLine("{0} to poprawny adres e-mail: {1}",
  arg0: email2,
  arg1: RozszerzeniaDlaString.AdresPoprawny(email2));

WriteLine("{0} to poprawny adres e-mail: {1}",
  arg0: email1,
  arg1: email1.AdresPoprawny());

WriteLine("{0} to poprawny adres e-mail: {1}",
  arg0: email2,
  arg1: email2.AdresPoprawny());

static void Henryk_Krzycz(object? sender, EventArgs e)
{
  if (sender is null) return;
  Osoba p = (Osoba)sender;
  WriteLine($"{p.Nazwisko} złości się na poziomie: {p.PoziomZlosci}.");
}
