{-# 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)