{-# OPTIONS --safe --without-K #-}

open import Prelude

module Generics.Telescope where

open import Agda.Builtin.Reflection public using (Visibility; visible; hidden; instance′)

infixr 5 _∷_
infixr 4 _++_

mutual

  data Tel : Level  Setω where
    []   : Tel lzero
    _∷_  : (A : Set ) (T : A  Tel ℓ')  Tel (  ℓ')
    _++_ : (T : Tel ) (U :  T ⟧ᵗ  Tel ℓ')  Tel (  ℓ')

  ⟦_⟧ᵗ : Tel   Set 
   []     ⟧ᵗ = 
   A   T ⟧ᵗ = Σ[ a  A ]  T a ⟧ᵗ
   T ++ U ⟧ᵗ = Σ[ t   T ⟧ᵗ ]  U t ⟧ᵗ

∷-syntaxᵗ : (A : Set ) (T : A  Tel ℓ')  Tel (  ℓ')
∷-syntaxᵗ = _∷_

syntax ∷-syntaxᵗ A  x  T) = [ x  A ] T

++-syntaxᵗ : (A : Tel ) (T :  A ⟧ᵗ  Tel ℓ')  Tel (  ℓ')
++-syntaxᵗ = _++_

syntax ++-syntaxᵗ T  x  U) = [[ x  T ]] U

data TelInfo (I : Set ) :  {ℓ'}  Tel ℓ'  Setω where
  []   : TelInfo I []
  _∷_  : {A : Set ℓ'} {T : A  Tel ℓ''}
        I  ((a : A)  TelInfo I (T a))  TelInfo I (A  T)
  _++_ : {T : Tel ℓ'} {U :  T ⟧ᵗ  Tel ℓ''}
        TelInfo I T  ((t :  T ⟧ᵗ)  TelInfo I (U t))  TelInfo I (T ++ U)

constTelInfo : {I : Set }  I  {T : Tel ℓ'}  TelInfo I T
constTelInfo i {[]    } = []
constTelInfo i {A   T} = i  λ _  constTelInfo i
constTelInfo i {T ++ U} = constTelInfo i ++ λ _  constTelInfo i

Curriedᵗ : (T : Tel )  TelInfo Visibility T  ( T ⟧ᵗ  Set ℓ')  Set (  ℓ')
Curriedᵗ []       _                X = X tt
Curriedᵗ (A   T) (visible    vs) X = ( a : A )  Curriedᵗ (T a) (vs a) λ t  X (a , t)
Curriedᵗ (A   T) (hidden     vs) X = { a : A }  Curriedᵗ (T a) (vs a) (curry X a)
Curriedᵗ (A   T) (instance′  vs) X =  a : A   Curriedᵗ (T a) (vs a) (curry X a)
Curriedᵗ (T ++ U) (vs ++ vs')      X = Curriedᵗ  T     vs     λ t 
                                       Curriedᵗ (U t) (vs' t) λ u  X (t , u)

curryᵗ : {T : Tel } {vs : TelInfo Visibility T} {X :  T ⟧ᵗ  Set ℓ'}
        ((t :  T ⟧ᵗ)  X t)  Curriedᵗ T vs X
curryᵗ {T = []    }                  f = f tt
curryᵗ {T = A   T} {visible    vs} f = λ a         curryᵗ λ u  f (a , u)
curryᵗ {T = A   T} {hidden     vs} f = curryᵗ (curry f _)
curryᵗ {T = A   T} {instance′  vs} f = curryᵗ (curry f _)
curryᵗ {T = T ++ U} {vs ++ vs'}      f = curryᵗ λ t  curryᵗ λ u  f (t , u)

uncurryᵗ : {T : Tel } {vs : TelInfo Visibility T} {X :  T ⟧ᵗ  Set ℓ'}
          Curriedᵗ T vs X  (t :  T ⟧ᵗ)  X t
uncurryᵗ {T = []    }                  f tt      = f
uncurryᵗ {T = A   T} {visible    vs} f (a , t) = uncurryᵗ (f   a  ) t
uncurryᵗ {T = A   T} {hidden     vs} f (a , t) = uncurryᵗ (f { a }) t
uncurryᵗ {T = A   T} {instance′  vs} f (a , t) = uncurryᵗ (f  a ⦄) t
uncurryᵗ {T = T ++ U} {vs ++ vs'}      f (t , u) = uncurryᵗ (uncurryᵗ f t) u

_^_ : Set    Set
A ^ zero  = 
A ^ suc n = A × (A ^ n)