module Codata.IO where

open import Codata.IO.Core public

import IO.Primitive as Prim
open import Codata.IO.Types hiding (module FFI) public
open import Agda.Builtin.Unit

Main : Set
Main = Prim.IO ⊤

open import Level
open import Size
open import Codata.Thunk using (Thunk; force)
open import Agda.Builtin.Equality
open import Data.Maybe.Base using (Maybe)
open import Function
open import Foreign.Haskell.Coerce

private
  variable
    a b ℓ : Level
    A : Set a
    B : Set b
    i : Size

map  : {B : Set b} {{_ : b ≤ˡ ℓ}} → (A → B) → IO′ ℓ A i → IO′ ℓ B i
map f (lift m)   = lift (m Prim.>>= λ a → Prim.return (f a))
map f (return a) = return (f a)
map f (bind m g) = bind m $ λ a → λ where .force → map f (g a .force)

delay : {A : Set a} {{_ : a ≤ˡ ℓ}} → Thunk (IO′ ℓ A) i → IO′ ℓ A i
delay m = bind m λ a → λ where .force → return a

infixr 1 _>>=_ _ᵗ>>=_ _>>=ᵗ_

_>>=_ : IO′ ℓ A i → (A → IO′ ℓ B i) → IO′ ℓ B i
m >>= f = bind (λ where .force → m) (λ a → λ where .force → f a)

_=<<_ : (A → IO′ ℓ B i) → IO′ ℓ A i → IO′ ℓ B i
f =<< m = m >>= f

_ᵗ>>=_ : Thunk (IO′ ℓ A) i → (A → IO′ ℓ B i) → IO′ ℓ B i
m ᵗ>>= f = bind m (λ a → λ where .force → f a)

_>>=ᵗ_ : IO′ ℓ A i → (A → Thunk (IO′ ℓ B) i) → IO′ ℓ B i
m >>=ᵗ f = bind (λ where .force → m) f

infixr 1 _>>_
infixl 1 _<<_ _<$_ _<$>_ _<*>_

_<$>_ : {B : Set b} {{_ : b ≤ˡ ℓ}} → (A → B) → IO′ ℓ A i → IO′ ℓ B i
f <$> m = m >>= λ a → return (f a)

_<$_ : {A : Set a} {{_ : a ≤ˡ ℓ}} → A → IO′ ℓ B i → IO′ ℓ A i
a <$ mb = mb >>= λ _ → return a

_<*>_ : {B : Set b} {{_ : b ≤ˡ ℓ}} → IO′ ℓ (A → B) i → IO′ ℓ A i → IO′ ℓ B i
f <*> m = f >>= (_<$> m)

_>>_ : ∀ {i} → IO′ ℓ A i → IO′ ℓ B i → IO′ ℓ B i
ma >> mb = ma >>= λ _ → mb

_<<_ : ∀ {i} {A : Set a} {{_ : a ≤ˡ ℓ}} → IO′ ℓ A i → IO′ ℓ B i → IO′ ℓ A i
ma << mb = ma >>= λ a → a <$ mb

module ListIO where

  open import Data.List.Base as List using (List; []; _∷_)

  sequence : {A : Set a} {{_ : a ≤ˡ ℓ}} →
             List (IO ℓ A) → IO ℓ (List A)
  sequence []         = return []
  sequence (mx ∷ mxs) = mx           >>= λ x →
                        sequence mxs >>= λ xs →
                        return (x ∷ xs)

  module _ {B : Set b} {{_ : b ≤ˡ ℓ}} where

    mapM : (A → IO ℓ B) → List A → IO ℓ (List B)
    mapM f xs = sequence (List.map f xs)

    mapM′ : (A → IO ℓ B) → List A → IO ℓ ⊤
    mapM′ f xs = tt <$ mapM f xs

    forM : List A → (A → IO ℓ B) → IO ℓ (List B)
    forM  = flip mapM

    forM′ : List A → (A → IO ℓ B) → IO ℓ ⊤
    forM′ = flip mapM′

module VecIO where

  open import Data.Vec.Base as Vec using (Vec; []; _∷_)

  sequence : ∀ {n} {A : Set a} {{_ : a ≤ˡ ℓ}} →
             Vec (IO ℓ A) n → IO ℓ (Vec A n)
  sequence []         = return []
  sequence (mx ∷ mxs) = mx           >>= λ x →
                        sequence mxs >>= λ xs →
                        return (x ∷ xs)

  module _ {n} {B : Set b} {{_ : b ≤ˡ ℓ}} where

    mapM : (A → IO ℓ B) → Vec A n → IO ℓ (Vec B n)
    mapM f xs = sequence (Vec.map f xs)

    mapM′ : (A → IO ℓ B) → Vec A n → IO ℓ ⊤
    mapM′ f xs = tt <$ mapM f xs

    forM : Vec A n → (A → IO ℓ B) → IO ℓ (Vec B n)
    forM  = flip mapM

    forM′ : Vec A n → (A → IO ℓ B) → IO ℓ ⊤
    forM′ = flip mapM′

module ColistIO where

  open import Codata.Colist as Colist using (Colist; []; _∷_)

  sequence : {A : Set a} {{_ : a ≤ˡ ℓ}} →
             Colist (IO′ ℓ A i) ∞ → IO′ ℓ (Colist A ∞) i
  sequence []         = return []
  sequence (mx ∷ mxs) =
    mx                                        >>= λ x →
    (λ where .force → sequence (mxs .force)) ᵗ>>= λ xs →
    return (x ∷ λ where .force → xs)

  module _  {B : Set b} {{_ : b ≤ˡ ℓ}} where

    mapM : (A → IO′ ℓ B i) → Colist A ∞ → IO′ ℓ (Colist B ∞) i
    mapM f xs = sequence (Colist.map f xs)

    mapM′ : (A → IO′ ℓ B i) → Colist A ∞ → IO′ ℓ ⊤ i
    mapM′ f xs = tt <$ mapM f xs

    forM : Colist A ∞ → (A → IO′ ℓ B i) → IO′ ℓ (Colist B ∞) i
    forM  = flip mapM

    forM′ : Colist A ∞ → (A → IO′ ℓ B i) → IO′ ℓ ⊤ i
    forM′ = flip mapM′

open import Agda.Builtin.Char
open import Data.String
open import Codata.Musical.Costring
open import System.FilePath.Posix
import Codata.IO.Primitive as Prim

open import Codata.IO.Types
  using ( BufferMode
        ; NoBuffering
        ; LineBuffering
        ; BlockBuffering
        )
  public
open import Codata.IO.Primitive
  using ( Handle
        ; stdin
        ; stdout
        ; stderr
        )
  public


hSetBuffering  : Handle → BufferMode → IO ℓ ⊤
hGetBuffering  : Handle → IO ℓ BufferMode
hFlush         : Handle → IO ℓ ⊤
interact       : (String → String) → IO ℓ ⊤
getChar        : IO ℓ Char
getLine        : IO ℓ String
getContents    : IO ℓ Costring
readFile       : ∀ {n} → FilePath n → IO ℓ Costring
writeFile      : ∀ {n} → FilePath n → Costring → IO ℓ ⊤
appendFile     : ∀ {n} → FilePath n → Costring → IO ℓ ⊤
putChar        : Char → IO ℓ ⊤
putCoStr       : Costring → IO ℓ ⊤
putCoStrLn     : Costring → IO ℓ ⊤
putStr         : String → IO ℓ ⊤
putStrLn       : String → IO ℓ ⊤
readFiniteFile : ∀ {n} → FilePath n → IO ℓ String

hSetBuffering h b = lift (coerce Prim.hSetBuffering h b)
hGetBuffering h   = coerce <$> lift (Prim.hGetBuffering h)
hFlush            = λ h → lift (Prim.hFlush h)
interact          = λ f → lift (Prim.interact f)
getChar           = lift Prim.getChar
getLine           = lift Prim.getLine
getContents       = lift Prim.getContents
readFile          = λ fp → lift (Prim.readFile (getFilePath fp))
writeFile         = λ fp cstr → lift (Prim.writeFile (getFilePath fp) cstr)
appendFile        = λ fp cstr → lift (Prim.appendFile (getFilePath fp) cstr)
putChar           = λ c → lift (Prim.putChar c)
putCoStr          = λ cstr → lift (Prim.putStr cstr)
putCoStrLn        = λ cstr → lift (Prim.putStrLn cstr)
putStr            = putCoStr ∘′ toCostring
putStrLn          = putCoStrLn ∘′ toCostring
readFiniteFile    = lift ∘′ Prim.readFiniteFile ∘′ getFilePath

{-# NON_TERMINATING #-}
run : IO ℓ A → Prim.IO A
run (lift io)  = io
run (return a) = Prim.return a
run (bind m f) = run (m .force) Prim.>>= λ a → run (f a .force)
