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

open import Prelude

module Generics.Description where
open import Prelude.List as List
open import Prelude.Sum  as Sum

open import Generics.Telescope

RecB : Set
RecB = List Level

ConB : Set
ConB = List (Level  RecB)

ConBs : Set
ConBs = List ConB

max-ℓ : List Level  Level
max-ℓ = foldr lzero _⊔_

maxMap : {A : Set}  (A  Level)  List A  Level
maxMap f = max-ℓ  List.map f

ρ-level : Level  RecB  Level
ρ-level (inl _ ) = lzero
ρ-level (inr rb) = max-ℓ rb

has-ρ? : Level  Level  RecB  Level
has-ρ?  (inl _) = lzero
has-ρ?  (inr _) = 

σ-level : Level  RecB  Level
σ-level (inl ) = 
σ-level (inr _) = lzero

max-π : ConB  Level
max-π = maxMap ρ-level

max-σ : ConB  Level
max-σ = maxMap σ-level

hasRec? : Level  ConB  Level
hasRec?  = maxMap (has-ρ? )

hasCon? : Level  ConBs  Level
hasCon?  = maxMap  _  )

maxMap-bound : {A : Set} (f : A  Level) { : Level}
              (∀ a  f a  )   as  maxMap f as  
maxMap-bound f ineq []       = refl
maxMap-bound f ineq (a  as) = cong₂ _⊔_ (ineq a) (maxMap-bound f ineq as)

hasRec?-bound :   cb  hasRec?  cb  
hasRec?-bound  []            = refl
hasRec?-bound  (inl ℓ'  cb) = hasRec?-bound  cb
hasRec?-bound  (inr rb  cb) = hasRec?-bound  cb

hasCon?-bound :   cbs  hasCon?  cbs  
hasCon?-bound  = maxMap-bound  _  )  _  refl)

private variable
    rb : RecB
    cb : ConB
    cbs cbs' : ConBs

module _ (I : Set ℓⁱ) where

  data RecD : RecB  Setω where
    ι : (i : I)  RecD []
    π : (A : Set ) (D : A  RecD rb)  RecD (  rb)

  data ConD : ConB  Setω where
    ι : (i : I)  ConD []
    σ : (A : Set ) (D : A  ConD cb)  ConD (inl    cb)
    ρ : (D : RecD rb) (E : ConD cb)    ConD (inr rb  cb)

  data ConDs : ConBs  Setω where
    []  : ConDs []
    _∷_ : (D : ConD cb) (Ds : ConDs cbs)  ConDs (cb  cbs)

  syntax π A  a  D) = π[ a  A ] D
  syntax σ A  a  D) = σ[ a  A ] D
  syntax ρ R        D  = ρ[     R ] D

record PDataD : Setω where
  constructor pdatad
  field
    alevel   : Level
    {plevel} : Level
    {ilevel} : Level
    {struct} : ConBs
  dlevel : Level
  dlevel = alevel  ilevel
  flevel : Level  Level
  flevel  = maxMap max-π struct  maxMap max-σ struct 
             maxMap (hasRec? ) struct  hasCon? ilevel struct
  field
     level-inequality  : maxMap max-π struct  maxMap max-σ struct  dlevel
    Param  : Tel plevel
    Index  :  Param ⟧ᵗ  Tel ilevel
    applyP : (ps :  Param ⟧ᵗ)  ConDs  Index ps ⟧ᵗ struct

level-pre-fixed-point : (D : PDataD)  PDataD.flevel D (PDataD.dlevel D)  PDataD.dlevel D
level-pre-fixed-point D =
  let cbs = PDataD.struct D; ℓᵃ = PDataD.alevel D; ℓⁱ = PDataD.ilevel D in
  begin
    maxMap max-π cbs  maxMap max-σ cbs 
    maxMap (hasRec? (ℓᵃ  ℓⁱ)) cbs  hasCon? ℓⁱ cbs  ℓᵃ  ℓⁱ
      ≡⟨ cong (maxMap max-π cbs  maxMap max-σ cbs ⊔_) (cong₂ _⊔_
        (maxMap-bound (hasRec? (ℓᵃ  ℓⁱ)) (hasRec?-bound (ℓᵃ  ℓⁱ)) cbs)
        (hasCon?-bound ℓⁱ cbs)) 
    maxMap max-π cbs  maxMap max-σ cbs  ℓᵃ  ℓⁱ
      ≡⟨ PDataD.level-inequality D 
    ℓᵃ  ℓⁱ
   where open ≡-Reasoning

record DataD : Setω where
  constructor datad
  field
    #levels : 
  Levels : Set
  Levels = Level ^ #levels
  field
    applyL : Levels  PDataD

module _ {I : Set ℓⁱ} where

  ⟦_⟧ʳ : RecD I rb  (I  Set )  Set (max-ℓ rb  )
   ι i   ⟧ʳ X = X i
   π A D ⟧ʳ X = (a : A)   D a ⟧ʳ X

  ⟦_⟧ᶜ : ConD I cb  (I  Set )  (I  Set (max-π cb  max-σ cb  hasRec?  cb  ℓⁱ))
   ι i   ⟧ᶜ X j = i  j
   σ A D ⟧ᶜ X j = Σ A λ a   D a ⟧ᶜ X j
   ρ D E ⟧ᶜ X j = Σ ( D ⟧ʳ X) λ _   E ⟧ᶜ X j

  ⟦_⟧ᶜˢ : ConDs I cbs  (I  Set )  (I  Set (maxMap max-π cbs  maxMap max-σ cbs 
                                                maxMap (hasRec? ) cbs  hasCon? ℓⁱ cbs))
   []     ⟧ᶜˢ X i = 
   D  Ds ⟧ᶜˢ X i =  D ⟧ᶜ X i   Ds ⟧ᶜˢ X i

⟦_⟧ᵖᵈ : (D : PDataD) {p :  PDataD.Param D ⟧ᵗ}
       let I =  PDataD.Index D p ⟧ᵗ in (I  Set )  (I  Set (PDataD.flevel D ))
 D ⟧ᵖᵈ {p} =  PDataD.applyP D p ⟧ᶜˢ

⟦_⟧ᵈ : (D : DataD) {ℓs : DataD.Levels D}  let Dᵖ = DataD.applyL D ℓs in
       {p :  PDataD.Param Dᵖ ⟧ᵗ}
      let I =  PDataD.Index Dᵖ p ⟧ᵗ in (I  Set )  (I  Set (PDataD.flevel Dᵖ ))
 D ⟧ᵈ {ℓs} =  DataD.applyL D ℓs ⟧ᵖᵈ

fmapʳ : {I : Set ℓⁱ} (D : RecD I rb) {X : I  Set ℓˣ} {Y : I  Set ℓʸ}
       ({i : I}  X i  Y i)   D ⟧ʳ X   D ⟧ʳ Y
fmapʳ (ι i  ) f x  = f x
fmapʳ (π A D) f xs = λ a  fmapʳ (D a) f (xs a)

fmapᶜ : {I : Set ℓⁱ} (D : ConD I cb) {X : I  Set ℓˣ} {Y : I  Set ℓʸ}
       ({i : I}  X i  Y i)  {i : I}   D ⟧ᶜ X i   D ⟧ᶜ Y i
fmapᶜ (ι i  ) f eq       = eq
fmapᶜ (σ A D) f (a , xs) = a , fmapᶜ (D a) f xs
fmapᶜ (ρ D E) f (x , xs) = fmapʳ D f x , fmapᶜ E f xs

fmapᶜˢ : {I : Set } (Ds : ConDs I cbs) {X : I  Set ℓˣ} {Y : I  Set ℓʸ}
        ({i : I}  X i  Y i)  {i : I}   Ds ⟧ᶜˢ X i   Ds ⟧ᶜˢ Y i
fmapᶜˢ (D  Ds) f (inl xs) = inl (fmapᶜ  D  f xs)
fmapᶜˢ (D  Ds) f (inr xs) = inr (fmapᶜˢ Ds f xs)

fmapᵖᵈ : (D : PDataD) {p :  PDataD.Param D ⟧ᵗ}  let I =  PDataD.Index D p ⟧ᵗ in
         {X : I  Set ℓˣ} {Y : I  Set ℓʸ}
        ({i : I}  X i  Y i)  {i : I}   D ⟧ᵖᵈ X i   D ⟧ᵖᵈ Y i
fmapᵖᵈ D {p} = fmapᶜˢ (PDataD.applyP D p)

fmapᵈ : (D : DataD) {ℓs : DataD.Levels D}  let Dᵖ = DataD.applyL D ℓs in
        {p :  PDataD.Param Dᵖ ⟧ᵗ}  let I =  PDataD.Index Dᵖ p ⟧ᵗ in
        {X : I  Set ℓˣ} {Y : I  Set ℓʸ}
       ({i : I}  X i  Y i)  {i : I}   D ⟧ᵈ X i   D ⟧ᵈ Y i
fmapᵈ D {ℓs} = fmapᵖᵈ (DataD.applyL D ℓs)

ExtEqʳ : {I : Set ℓⁱ} (D : RecD I rb) {X : I  Set ℓˣ}
         (xs xs' :  D ⟧ʳ X)  Set (max-ℓ rb  ℓˣ)
ExtEqʳ (ι i  ) x  x'  = x  x'
ExtEqʳ (π A D) xs xs' = (a : A)  ExtEqʳ (D a) (xs a) (xs' a)

Finitaryʳ : RecB  Set
Finitaryʳ = _≡ []

Finitaryᶜ : ConB  Set
Finitaryᶜ = All Sum.[ const  , Finitaryʳ ]

Finitaryᶜˢ : ConBs  Set
Finitaryᶜˢ = All Finitaryᶜ

Finitaryᵖᵈ : PDataD  Set
Finitaryᵖᵈ D = Finitaryᶜˢ (PDataD.struct D)

Finitary : DataD  Set
Finitary D =  {ℓs}  Finitaryᵖᵈ (DataD.applyL D ℓs)