{-| 'String'-based 'IO' operations.

    Note that 'String's are very inefficient, and I will release future separate
    packages with 'ByteString' and 'Text' operations.  I only provide these to
    allow users to test simple I/O without requiring additional library
    dependencies.
-}

module Control.Proxy.Prelude.IO (
    -- * Standard I/O
    -- ** Input
    getLineS,
    getLineC,
    readLnS,
    readLnC,
    -- ** Output
    printB,
    printD,
    printU,
    putStrLnB,
    putStrLnD,
    putStrLnU,
    -- ** Interaction
    promptS,
    promptC,
    -- * Handle I/O
    -- ** Input
    hGetLineD,
    hGetLineU,
    -- ** Output
    hPrintB,
    hPrintD,
    hPrintU,
    hPutStrLnB,
    hPutStrLnD,
    hPutStrLnU
    ) where

import Control.Monad (forever)
import Control.Monad.Trans.Class (lift)
import Control.Proxy.Prelude.Kleisli (foreverK)
import Control.Proxy.Core (Proxy, Client, Server)
import Control.Proxy.Class (request, respond)
import System.IO (Handle, hGetLine, hPutStr, hPutStrLn, hPrint, stdin, stdout)

-- | Get input from 'stdin' one line at a time and send \'@D@\'ownstream
getLineS :: y' -> Proxy x' x y' String IO r
getLineS _ = forever $ do
    str <- lift getLine
    respond str

-- | Get input from 'stdin' one line at a time and send \'@U@\'pstream
getLineC :: y' -> Proxy String x y' y IO r
getLineC _ = forever $ do
    str <- lift getLine
    request str

-- | 'read' input from 'stdin' one line at a time and send \'@D@\'ownstream
readLnS :: (Read a) => y' -> Proxy x' x y' a IO r
readLnS _ = forever $ do
    a <- lift readLn
    respond a

-- | 'read' input from 'stdin' one line at a time and send \'@U@\'pstream
readLnC :: (Read a) => y' -> Proxy a x y' y IO r
readLnC _ = forever $ do
    a <- lift readLn
    request a

{-| 'print's all values flowing through it to 'stdout'

    Prefixes upstream values with \"@U: @\" and downstream values with \"@D: @\"
-}
printB :: (Show a, Show a') => a' -> Proxy a' a a' a IO r
printB = foreverK $ \a' -> do
    lift $ do
        putStr "U: "
        print a'
    a <- request a'
    lift $ do
        putStr "D: "
        print a
    respond a

-- | 'print's all values flowing \'@D@\'ownstream to 'stdout'
printD :: (Show a) => x -> Proxy x a x a IO r
printD = foreverK $ \x -> do
    a <- request x
    lift $ print a
    respond a

-- | 'print's all values flowing \'@U@\'pstream to 'stdout'
printU :: (Show a') => a' -> Proxy a' x a' x IO r
printU = foreverK $ \a' -> do
    lift $ print a'
    x <- request a'
    respond x

{-| 'putStrLn's all values flowing through it to 'stdout'

    Prefixes upstream values with \"@U: @\" and downstream values with \"@D: @\"
-}
putStrLnB :: String -> Proxy String String String String IO r
putStrLnB = foreverK $ \a' -> do
    lift $ do
        putStr "U: "
        putStrLn a'
    a <- request a'
    lift $ do
        putStr "D: "
        putStrLn a
    respond a

-- | 'putStrLn's all values flowing \'@D@\'ownstream to 'stdout'
putStrLnD :: x -> Proxy x String x String IO r
putStrLnD = foreverK $ \x -> do
    a <- request x
    lift $ putStrLn a
    respond a

-- | 'putStrLn's all values flowing \'@U@\'pstream to 'stdout'
putStrLnU :: String -> Proxy String x String x IO r
putStrLnU = foreverK $ \a' -> do
    lift $ putStrLn a'
    x <- request a'
    respond x

-- | Convert 'stdin'/'stdout' into a line-based 'Server'
promptS :: String -> Proxy x' x String String IO r
promptS = foreverK $ \send -> do
    recv <- lift $ do
        putStrLn send
        getLine
    respond recv

-- | Convert 'stdin'/'stdout' into a line-based 'Client'
promptC :: y' -> Proxy String String y' y IO r
promptC _ = forever $ do
    send <- lift getLine
    recv <- request send
    lift $ putStrLn recv

-- | Get input from a handle one line at a time and send \'@D@\'ownstream
hGetLineD :: Handle -> y' -> Proxy x' x y' String IO r
hGetLineD h _ = forever $ do
    str <- lift $ hGetLine h
    respond str

-- | Get input from a handle one line at a time and send \'@U@\'pstream
hGetLineU :: Handle -> y' -> Proxy String x y' y IO r
hGetLineU h _ = forever $ do
    str <- lift $ hGetLine h
    request str

{-| 'print's all values flowing through it to a 'Handle'

    Prefixes upstream values with \"@U: @\" and downstream values with \"@D: @\"
-}
hPrintB :: (Show a, Show a') => Handle -> a' -> Proxy a' a a' a IO r
hPrintB h = foreverK $ \a' -> do
    lift $ do
        hPutStr h "U: "
        hPrint h a'
    a <- request a'
    lift $ do
        hPutStr h "D: "
        hPrint h a
    respond a

-- | 'print's all values flowing \'@D@\'ownstream to a 'Handle'
hPrintD :: (Show a) => Handle -> x -> Proxy x a x a IO r
hPrintD h = foreverK $ \x -> do
    a <- request x
    lift $ hPrint h a
    respond a

-- | 'print's all values flowing \'@U@\'pstream to a 'Handle'
hPrintU :: (Show a') => Handle -> a' -> Proxy a' x a' x IO r
hPrintU h = foreverK $ \a' -> do
    lift $ hPrint h a'
    x <- request a'
    respond x

{-| 'putStrLn's all values flowing through it to a 'Handle'

    Prefixes upstream values with \"@U: @\" and downstream values with \"@D: @\"
-}
hPutStrLnB :: Handle -> String -> Proxy String String String String IO r
hPutStrLnB h = foreverK $ \a' -> do
    lift $ do
        hPutStr h "U: "
        hPutStrLn h a'
    a <- request a'
    lift $ do
        hPutStr h "D: "
        hPutStrLn h a
    respond a

-- | 'putStrLn's all values flowing \'@D@\'ownstream to a 'Handle'
hPutStrLnD :: Handle -> x -> Proxy x String x String IO r
hPutStrLnD h = foreverK $ \x -> do
    a <- request x
    lift $ hPutStrLn h a
    respond a

-- | 'putStrLn's all values flowing \'@U@\'pstream to a 'Handle'
hPutStrLnU :: Handle -> String -> Proxy String x String x IO r
hPutStrLnU h = foreverK $ \a' -> do
    lift $ hPutStrLn h a'
    x <- request a'
    respond x