module Generic.Examples.Printing where

open import Size
open import Coinduction hiding ()
open import Data.Unit
open import Data.Bool
open import Data.Product
open import Data.Nat.Base
open import Data.Nat.Show as Nat
open import Data.List.Base as L hiding ([_] ; _++_ ; lookup)
open import Data.Char
open import Data.String using (String ; _++_ ; fromList ; toList)
open import Data.Stream as Str hiding (_++_ ; lookup)
open import Category.Monad
open import Category.Monad.State
open import Function

module ST = RawMonadState (StateMonadState (Stream String))
open ST

M = State (Stream String)

open import var hiding (get)
open import environment as E
open import varlike
open import Generic.Syntax as S
open import Generic.Semantics

module _ {I : Set} where

 record Name (i : I) (Γ : List I) : Set where
   constructor mkN; field getN : String

 record Printer (i : I) (Γ : List I) : Set where
   constructor mkP; field getP : M String

open Name
open Printer

module _ {I : Set} where

 fresh : {i : I} {Γ : List I}  M (Name i Γ)
 fresh =  get             >>=  λ nms  
          put (tail nms)  >>=  λ _    
          return $ mkN $ head nms

module _ {I : Set} (d : Desc I) where

 Pieces : List I  I ─Scoped
 Pieces []  i Γ = String
 Pieces Δ   i Γ = (Δ ─Env)  _ _  String) [] × String

 record Display : Set where
   constructor mkD
   field getD :  {i Γ}   d  Pieces i Γ  String

open Display

module _ {I : Set} {d : Desc I} where

 printing : Display d  Sem d Name Printer
 printing dis = record
   { th^𝓥  = λ n _  mkN (getN n)
   ; var   = λ n  mkP (return (getN n))
   ; alg   = λ {i} {Γ} v  mkP
           $ let p : M ( d  (Pieces d) i Γ)
                 p = S.traverse rawIApplicative d (fmap d reify^M v)
             in getD dis ST.<$> p
   } where

   vl^StName : VarLike {I}  i Γ  State (Stream String) (Name i Γ))
   vl^StName = record
     { new   = fresh
     ; th^𝓥  = λ st _  mkN  getN ST.<$> st }

   reify^M : {Γ : List I} (Δ : List I) (i : I) 
             Kripke Name Printer Δ i Γ 
             M (Pieces d Δ i Γ)
   reify^M []         i = getP
   reify^M Δ@(_  _)  i = λ f  let σ = freshˡ vl^StName _
                                in  E.traverse rawIApplicative σ >>= λ ρ 
                                    getP (f (freshʳ vl^Var Δ) ρ) >>= λ b 
                                    return ((getN E.<$> ρ) , b)

 print : Display d  {i : I}  Tm d  i []  String
 print dis t = proj₁ $ getP (Sem.closed (printing dis) t) names where

  flatten : {A : Set}  Stream (A × List A)  Stream A
  flatten ((a , as) Str.∷ aass) = go a as ( aass) where
    go : {A : Set}  A  List A  Stream (A × List A)  Stream A
    go a []        aass = a   flatten aass
    go a (b  as)  aass = a   go b as aass

  names : Stream String
  names = flatten $ Str.zipWith cons letters
                  $ ""   Str.map Nat.show (allNatsFrom 0)
    where

      cons : (Char × List Char)  String  (String × List String)
      cons (c , cs) suffix = appendSuffix c , L.map appendSuffix cs where
        appendSuffix : Char  String
        appendSuffix c  = fromList (c  []) ++ suffix

      letters = Str.repeat $ 'a' , toList "bcdefghijklmnopqrstuvwxyz"

      allNatsFrom :   Stream 
      allNatsFrom k = k   allNatsFrom (1 + k)


open import Generic.Examples.UntypedLC

printLC : Display UTLC
getD printLC = case  { (f , t , _)     f ++ "(" ++ t ++ ")" })
                     { ((x , b) , _)   "λ" ++ lookup x z ++ ". " ++ b })

open import Agda.Builtin.Equality

_ : print printLC `id  "λa. a"
_ = refl

open import Generic.Examples.HuttonsRazor

printHR : Display HuttRaz
getD printHR = case (Nat.show  proj₁)
                     { (m , n , _)  m ++ " + " ++ n })

_ : print printHR ((lit 2 [+] lit 6) [+] lit 0)  "2 + 6 + 0"
_ = refl

open import Generic.Examples.SystemF as SystemF

printSF : Display system-F
getD printSF  = case   { (σ , τ , _)     σ ++ " → " ++ τ })
              $ case   { ((x , b) , _)   "∀" ++ lookup x z ++ ". " ++ b })
              $ case   { (f , t , _)     f ++ "(" ++ t ++ ")" })
              $ case   { ((x , b) , _)   "λ" ++ lookup x z ++ ". " ++ b })
              $ case   { (f , T , _)     f ++ "(" ++ T ++ ")" })
                       { ((x , b) , _)   "Λ" ++ lookup x z ++ ". " ++ b })

_ : print printSF SystemF.`id  "Λa. λb. b"
_ = refl