--
import IO
import Concurrent

{-
openFile :: FilePath -> IOMode -> IO Handle
hClose :: Handle -> IO ()
data IOMode  = ReadMode | WriteMode | AppendMode
                  deriving (Eq, Ord, Ix, Bounded, Enum, Read, Show)

-- When in WriteMode
hPutChar :: Handle -> Char -> IO ()
hPutStr :: Handle -> String -> IO ()
hPutStrLn :: Handle -> String -> IO ()
hPrint :: Show a => Handle -> a -> IO ()

-- When in ReadMode
hGetChar :: Handle -> IO Char
hGetLine :: Handle -> IO String

-- Entire Contents returned as String
hGetContents :: Handle -> IO String

-- Why use Handle's when we have functions like:
writeFile :: FilePath -> String -> IO ()
appendFile :: FilePath -> String -> IO ()

-- Predefined standard Channels
stdin, stdout, stderr :: Handle


-- Error Handling while doing IO
isEOFError :: IOError -> Bool
ioError :: IOError -> IO a                      -- Raise an IOError
catch :: IO a -> (IOError -> IO a) -> IO a
-}

getChar' :: IO Char
getChar' = catch getChar (\ e -> return '\n')


getChar2 :: IO Char
getChar2 = catch getChar (\ e -> if isEOFError e then return '\n' else ioError e)

getLine' :: IO String
getLine' = catch getLine'' (\ e -> return ("Error: " ++ show e))
  where getLine'' =
          do { c <- getChar2
             ; if c == '\n'
                  then return ""
                  else do { l <- getLine'
                          ; return (c:l)
                          }
             }


getAndOpenFile :: String -> IOMode -> IO Handle
getAndOpenFile prompt mode =
  do { putStr prompt
     ; name <- getLine
     ; catch (openFile name mode)
             (\e -> do { putStrLn ("Cannot open: "++name)
                       ; print e
                       ; getAndOpenFile prompt mode
                       })
     }

main =
  do { fromHandle <- getAndOpenFile "Copy from: " ReadMode
     ; toHandle <- getAndOpenFile "Copy to: " WriteMode
     ; contents <- hGetContents fromHandle
     ; hPutStr toHandle contents
     ; hClose fromHandle
     ; hClose toHandle
     ; putStr "Done\n"
     }

-- First Class Channels

{-
-- First Class Channel Operations
newChan :: IO (Chan a)
writeChan :: Chan a -> a -> IO ()
readChan :: Chan a -> IO a
getChanContents :: Chan a -> IO [a]
isEmptyChan :: Chan a -> IO Bool
-}

ex1 =
 do { c <- newChan
    ; writeChan c 'a'
    ; writeChan c 'b'
    ; a <- readChan c
    ; b <- readChan c
    ; print [a,b]
    ; return [a,b]
    }


-- forkIO :: IO () -> IO ()

ex2 :: IO()
ex2 = do { c1 <- newChan :: IO(Chan Int)
         ; c2 <- newChan :: IO(Chan Int)
         ; forkIO (client c1 c2)
         ; forkIO (server c2 c1)
         }

client :: Chan Int -> Chan Int -> IO ()
client cin cout =
  do { writeChan cout 1
     ; loop
     }
 where loop = do { c <- readChan cin
                 ; print c
                 ; writeChan cout c
                 ; loop
                 }

server :: Chan Int -> Chan Int -> IO ()
server cin cout =
   do loop
 where loop = do { c <- readChan cin
                 ; writeChan cout (c+1)
                 ; loop
                 }

--