{-# OPTIONS --cubical #-}
open import Cubical.Foundations.Prelude
open import Cubical.Data.Sigma
open import Cubical.Data.FinData hiding (eq)
open import Cubical.HITs.PropositionalTruncation hiding (map)
open import Cubical.HITs.PropositionalTruncation.Monad
open import Realizability.CombinatoryAlgebra
open import Realizability.ApplicativeStructure

module Realizability.Assembly.Exponentials {} {A : Type } (ca : CombinatoryAlgebra A) where

open CombinatoryAlgebra ca
open Realizability.CombinatoryAlgebra.Combinators ca renaming (i to Id; ia≡a to Ida≡a)
open import Realizability.Assembly.Base ca
open import Realizability.Assembly.Morphism ca
open import Realizability.Assembly.BinProducts ca

-- Exponential objects
_⇒_ : {A B : Type }  (as : Assembly A)  (bs : Assembly B)  Assembly (AssemblyMorphism as bs)
(as  bs) .isSetX = isSetAssemblyMorphism as bs
(as  bs) ._⊩_ r f = tracks {xs = as} {ys = bs} r (f .map)
_⇒_ {A} {B} as bs .⊩isPropValued r f = isPropTracks {X = A} {Y = B} {xs = as} {ys = bs}  r (f .map)
(as  bs) .⊩surjective f = f .tracker

eval : {X Y : Type }  (xs : Assembly X)  (ys : Assembly Y)  AssemblyMorphism ((xs  ys)  xs) ys
eval xs ys .map (f , x) = f .map x
eval {X} {Y} xs ys .tracker =
        (s  (s  (k  pr₁)  Id)  (s  (k  pr₂)  Id))
       ,  (f , x) r r⊩fx  subst
                y  y ⊩Y (f .map x))
               (sym (tracker⨾r≡pr₁r⨾pr₂r (f , x) r r⊩fx))
               (pr₁r⨾pr₂rTracks (f , x) r r⊩fx))
       ∣₁ where
          _⊩Y_ = ys ._⊩_
          module _ (fx : (AssemblyMorphism xs ys) × X)
                   (r : A)
                   (r⊩fx : ((xs  ys)  xs) ._⊩_ r (fx .fst , fx .snd)) where
            f = fx .fst
            x = fx .snd
                          
            pr₁r⨾pr₂rTracks : (pr₁  r  (pr₂  r)) ⊩Y (f .map x)
            pr₁r⨾pr₂rTracks = r⊩fx .fst x (pr₂  r) (r⊩fx .snd)
                          
            tracker⨾r≡pr₁r⨾pr₂r : s  (s  (k  pr₁)  Id)  (s  (k  pr₂)  Id)  r  (pr₁  r)  (pr₂  r)
            tracker⨾r≡pr₁r⨾pr₂r =
              s  (s  (k  pr₁)  Id)  (s  (k  pr₂)  Id)  r
                ≡⟨ sabc≡ac_bc _ _ _  
              (s  (k  pr₁)  Id  r)  (s  (k  pr₂)  Id  r)
                ≡⟨ cong  x  x  (s  (k  pr₂)  Id  r)) (sabc≡ac_bc _ _ _)  
              (k  pr₁  r  (Id  r))  (s  (k  pr₂)  Id  r)
                ≡⟨ cong  x  (k  pr₁  r  (Id  r))  x) (sabc≡ac_bc _ _ _) 
              (k  pr₁  r  (Id  r))  (k  pr₂  r  (Id  r))
                ≡⟨ cong  x  (x  (Id  r))  (k  pr₂  r  (Id  r))) (kab≡a _ _) 
              (pr₁  (Id  r))  (k  pr₂  r  (Id  r))
                ≡⟨ cong  x  (pr₁  x)  (k  pr₂  r  (Id  r))) (Ida≡a r) 
              (pr₁  r)  (k  pr₂  r  (Id  r))
                ≡⟨ cong  x  (pr₁  r)  (x  (Id  r))) (kab≡a _ _)  
              (pr₁  r)  (pr₂  (Id  r))
                ≡⟨ cong  x  (pr₁  r)  (pr₂  x)) (Ida≡a r) 
              (pr₁  r)  (pr₂  r)
              

module _ {X Y Z : Type }
         {xs : Assembly X}
         {ys : Assembly Y}
         {zs : Assembly Z}
         (f : AssemblyMorphism (zs  xs) ys) where
         theEval = eval {X} {Y} xs ys
         ⇒isExponential : ∃![ g  AssemblyMorphism zs (xs  ys) ]
                           g , identityMorphism xs   theEval  f
         ⇒isExponential = uniqueExists  where
                                           .map z  λ where
                                                        .map x  f .map (z , x)
                                                        .tracker  do
                                                                    (f~ , f~tracks)  f .tracker
                                                                    (z~ , z~realizes)  zs .⊩surjective z
                                                                    return ( (s  (k  f~)  (s  (k  (pair  z~))  Id)
                                                                           , λ x aₓ aₓ⊩x
                                                                            subst  k  k ⊩Y (f .map (z , x)))
                                                                             (sym (eq f~ f~tracks z (z~ , z~realizes) x aₓ aₓ⊩x))
                                                                             (pair⨾z~⨾aₓtracks f~ f~tracks z (z~ , z~realizes) x aₓ aₓ⊩x)))
                                           .tracker  do
                                                       (f~ , f~tracker)  f .tracker
                                                       -- λ* x. λ* y. f~ ⨾ (pair ⨾ x ⨾ y)
                                                       let
                                                         realizer : Term as 2
                                                         realizer = ` f~ ̇ (` pair ̇ # one ̇ # zero)
                                                       return
                                                         (λ*2 realizer ,
                                                          z a a⊩z x b b⊩x 
                                                           subst
                                                              r'  r' ⊩[ ys ] (f .map (z , x)))
                                                             (sym (λ*2ComputationRule realizer a b))
                                                             (f~tracker
                                                               (z , x)
                                                               (pair  a  b)
                                                               ((subst  r'  r' ⊩[ zs ] z) (sym (pr₁pxy≡x _ _)) a⊩z) ,
                                                                (subst  r'  r' ⊩[ xs ] x) (sym (pr₂pxy≡y _ _)) b⊩x))))))
                                        (AssemblyMorphism≡ _ _ (funExt  (z , x)  refl)))
                                         g  isSetAssemblyMorphism _ _ ( g , identityMorphism xs   theEval) f)
                                        λ g g×id⊚eval≡f  AssemblyMorphism≡ _ _
                                                          (funExt  z  AssemblyMorphism≡ _ _
                                                                         (funExt  x  λ i  g×id⊚eval≡f (~ i) .map (z , x))))) where
                         _⊩X_ = xs ._⊩_
                         _⊩Y_ = ys ._⊩_
                         _⊩Z_ = zs ._⊩_
                         _⊩Z×X_ = (zs  xs) ._⊩_
                         Z×X = Z × X
                         module _ (f~ : A)
                                   (f~tracks : (∀ (zx : Z×X) (r : A) (rRealizes : (r ⊩Z×X zx))  ((f~  r) ⊩Y (f .map zx))))
                                   (z : Z)
                                   (zRealizer : Σ[ z~  A ] (z~ ⊩Z z))
                                   (x : X)
                                   (aₓ : A)
                                   (aₓ⊩x : aₓ ⊩X x) where
                            z~ : A
                            z~ = zRealizer .fst
                            z~realizes = zRealizer .snd

                            eq : s  (k  f~)  (s  (k  (pair  z~))  Id)  aₓ  f~  (pair  z~  aₓ)
                            eq =
                              s  (k  f~)  (s  (k  (pair  z~))  Id)  aₓ
                                ≡⟨ sabc≡ac_bc _ _ _ 
                              (k  f~  aₓ)  (s  (k  (pair  z~))  Id  aₓ)
                                ≡⟨ cong  x  x  (s  (k  (pair  z~))  Id  aₓ)) (kab≡a f~ aₓ) 
                              f~  (s  (k  (pair  z~))  Id  aₓ)
                                ≡⟨ cong  x  f~  x) (sabc≡ac_bc _ _ _) 
                              f~  ((k  (pair  z~)  aₓ)  (Id  aₓ))
                                ≡⟨ cong  x  f~  (x  (Id  aₓ))) (kab≡a (pair  z~) aₓ) 
                              f~  (pair  z~  (Id  aₓ))
                                ≡⟨ cong  x  f~  (pair  z~  x)) (Ida≡a aₓ) 
                              f~  (pair  z~  aₓ)
                                

                            pair⨾z~⨾aₓtracks : (f~  (pair  z~  aₓ)) ⊩Y (f .map (z , x))
                            pair⨾z~⨾aₓtracks =
                              f~tracks
                                (z , x)
                                (pair  z~  aₓ)
                                ( (subst  y  y ⊩Z z) (sym (pr₁pxy≡x z~ aₓ)) z~realizes)
                                , (subst  y  y ⊩X x) (sym (pr₂pxy≡y z~ aₓ)) aₓ⊩x))