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)