//Rozdział 22.
//Stan


//Przejścia między stanami zależne od stanu

public class Switch
{
    public State State = new OffState();
}

public abstract class State
{
    public virtual void On(Switch sw)
    {
        Console.WriteLine("Światło jest już włączone.");
    }
    public virtual void Off(Switch sw)
    {
        Console.WriteLine("Światło jest już wyłączone.");
    }
}

public class OnState : State
{
    public OnState()
    {
        Console.WriteLine("Światło włączone.");
    }
    public override void Off(Switch sw)
    {
        Console.WriteLine("Wyłączanie światła...");
        sw.State = new OffState();
    }
}

//podobnie dla stanu Off

public class Switch
{
    public State State = new OffState();
    public void On() { State.On(this); }
    public void Off() { State.Off(this); }
}

LightSwitch ls = new LightSwitch(); // Światło wyłączone
ls.On(); // Włączanie światła…
//Światło włączone
ls.Off(); // Wyłączanie światła …
//Światło wyłączone
ls.Off(); //Światło jest już wyłączone

//Maszyna stanów — „samoróbka”

public enum State
{
    OffHook,
    Connecting,
    Connected,
    OnHold
}

public enum Trigger
{
    CallDialed,
    HungUp,
    CallConnected,
    PlacedOnHold,
    TakenOffHold,
    LeftMessage
}
 private static Dictionary<State, List<(Trigger, State)>> rules
    = new Dictionary<State, List<(Trigger, State)>>() { /* todo */ }
 private static Dictionary<State, List<(Trigger, State)>> rules
= new Dictionary<State, List<(Trigger, State)>>
{
    [State.OffHook] = new List<(Trigger, State)>
    {
        (Trigger.CallDialed, State.Connecting)
    },
    [State.Connecting] = new List<(Trigger, State)>
    {
        (Trigger.HungUp, State.OffHook),
        (Trigger.CallConnected, State.Connected)
    },
    //tutaj więcej reguł

};

State state = State.OffHook, exitState = State.OnHook;
 do
{
    Console.WriteLine($"Telefon jest obecnie w stanie {state}");
    Console.WriteLine("Wybierz wyzwalacz:");
    for (var i = 0; i < rules[state].Count; i++)
    {
        var (t, _) = rules[state][i];
        Console.WriteLine($"{i}. {t}");
    }
    int input = int.Parse(Console.ReadLine());
    var (_, s) = rules[state][input];
    state = s;
} while (state != exitState);
Console.WriteLine("Zakończyliśmy używanie telefonu.");

// Maszyny stanów z wykorzystaniem biblioteki Stateless

var call = new StateMachine<State, Trigger>(State.OffHook);
phoneCall.Configure(State.OffHook)
    .Permit(Trigger.CallDialed, State.CallConnected);
//i tak dalej; aby spowodować przejście, korzystamy z następującego kodu
call.Fire(Trigger.CallDialed); // call.State ma teraz wartość State.CallConnected

// Typy, akcje i ignorowanie przejść
// enum Trigger { On, Off }

var light = new StateMachine<bool, Trigger>(false);

light.Configure(false) // jeśli światło jest wyłączone…
    .Permit(Trigger.On, true) //możemy je włączyć
    .Ignore(Trigger.Off); // ale jeśli jest już wyłączone, to nie wyłączamy go ponownie

// to samo, gdy światło jest włączone
light.Configure(true)
    .Permit(Trigger.Off, false)
    .Ignore(Trigger.On)
    .OnEntry(() => timer.Start())
    .OnExit(() => timer.Stop()); //obliczamy czas przebywania w tym stanie

light.Fire(Trigger.On); // Włączenie światła
light.Fire(Trigger.Off); // Wyłączenie światła
light.Fire(Trigger.Off); // Światło jest już wyłączone!
 Ponowne wejście w ten sam stan
 var light = new StateMachine<bool, Trigger>(false);

light.Configure(false) // jeśli światło jest wyłączone…
    .Permit(Trigger.On, true) //możemy je włączyć
    .OnEntry(transition =>
    {
        if (transition.IsReentry)
            WriteLine("Światło już jest wyłączone!");
        else
            WriteLine("Wyłączam światło");
    })
    .PermitReentry(Trigger.Off);

//to samo, gdy światło jest włączone

light.Fire(Trigger.On); // Włączam światło
light.Fire(Trigger.Off); // Wyłączam światło
light.Fire(Trigger.Off); // Światło już jest wyłączone!

// Stany hierarchiczne

 phoneCall.Configure(State.OnHold)
    .SubstateOf(State.Connected)
    // itd.

// Dodatkowe własności
 phoneCall.Configure(State.OffHook)
    .PermitIf(Trigger.CallDialled, State.Connecting,
    () => IsValidNumber)
    .PermitIf(Trigger.CallDialled, State.Beeping,
    () => !IsValidNumber);

var notifyTrigger = workflow.SetTriggerParameters<string>(Trigger.Notify);
workflow.Configure(State.Notified)
    .OnEntryFrom(assignTrigger, email => SendEmail(email));
workflow.Fire(notifyTrigger, "foo@bar.com");
 var stateMachine = new StateMachine<State, Trigger>(
    () => database.ReadState(),
    s => database.WriteState(s));
 
