Library Io.UseCase

Require Import Coq.Logic.JMeq.
Require C.
Require Run.

Record t {E A} (x : C.t E A) : Type := New {
  P : Type;
  v : PA;
  r : p, Run.t x (v p) }.
Arguments New {E A x} _ _ _.
Arguments P {E A x} _.
Arguments v {E A x} _ _.
Arguments r {E A x} _ _.

Module Generalize.
  Definition t {E A} {x : C.t E A} (u1 u2 : UseCase.t x) : Prop :=
     (p2 : P u2), (p1 : P u1), JMeq (r u1 p1) (r u2 p2).
End Generalize.

Fixpoint general {E A} (x : C.t E A) : UseCase.t x.
  destruct x as [A v | c | A B x f | A x1 x2 | A B x y].
  - refine (UseCase.New unit (fun _v) (fun __)).
    apply Run.Ret.
  - refine (UseCase.New (Effect.answer E c) (fun aa) (fun a_)).
    apply (Run.Call c a).
  - destruct (general _ _ x) as [P_x v_x r_x].
    refine (let g_f := fun v_xgeneral _ _ (f v_x) in _).
    refine (
      UseCase.New {p_x : P_x & UseCase.P (g_f (v_x p_x))}
        (fun p
          let (p_x, p_f) := p in
          UseCase.v (g_f (v_x p_x)) p_f)
        (fun p_)).
    destruct p as [p_x p_f].
    apply (Run.Let (r_x p_x) (UseCase.r (g_f (v_x p_x)) p_f)).
  - destruct (general _ _ x1) as [P1 v1 r1].
    destruct (general _ _ x2) as [P2 v2 r2].
    refine (
      UseCase.New (P1 + P2)
        (fun p
          match p with
          | inl p1v1 p1
          | inr p2v2 p2
          end)
        (fun p_)).
    destruct p as [p1 | p2].
    + apply (Run.ChooseLeft (r1 p1)).
    + apply (Run.ChooseRight (r2 p2)).
  - destruct (general _ _ x) as [P_x v_x r_x].
    destruct (general _ _ y) as [P_y v_y r_y].
    refine (
      UseCase.New (P_x × P_y)
        (fun p
          let (p_x, p_y) := p in
          (v_x p_x, v_y p_y))
        (fun p_)).
    destruct p as [p_x p_y].
    apply (Run.Join (r_x p_x) (r_y p_y)).
Defined.

Module I.
  Record t {E A} (x : C.I.t E A) : Type := New {
    P : Type;
    v : PA;
    r : p, Run.I.t x (v p) }.
  Arguments New {E A x} _ _ _.
  Arguments P {E A x} _.
  Arguments v {E A x} _ _.
  Arguments r {E A x} _ _.

  Module Generalize.
    Definition t {E A} {x : C.I.t E A} (u1 u2 : UseCase.I.t x) : Prop :=
       (p2 : P u2), (p1 : P u1), Run.I.Eq.t (r u1 p1) (r u2 p2).
  End Generalize.
End I.