-- The type `IO a` is thought of as a list of instructions for the operating
-- system. Following the instructions results in a value of type `a`.
--
-- This file makes use of the following primitive instructions:
--
--     readFile  :: FilePath -> IO String
--     writeFile :: FilePath -> String -> IO ()
--     getChar   :: IO Char
--
-- FilePath is defined as
--
--     type FilePath = String
--
-- () is pronounced "unit" and can be thought of as an empty tuple. () is a type
-- that has a single value, and this value is also written as ().
--
-- () is used when there is no other sensible value to return.
--
-- Other useful standard functions are:
--
--     putStr   :: String -> IO ()
--     putStrLn :: String -> IO ()



import Data.List



-- `writeTwoFiles f s` produces two files named `f++"1"` and `f++2` with the
-- same content `s`
-- (or rather, returns instructions to produce the files)
writeTwoFiles :: FilePath -> String -> IO ()
writeTwoFiles file s =
  do writeFile (file ++ "1") s
     writeFile (file ++ "2") s

-- `copyFile f1 f2` copies the file `f1` to the new file `f2`
-- (or rather, produces instructions copy the file)
copyFile :: FilePath -> FilePath -> IO ()
copyFile from to =
  do contents <- readFile from
     writeFile to contents

-- `catFile f1 f2` reads the files `f1` and `f2` and prints their content on the
-- terminal
-- (or rather, produces instructions print the content)
catFiles :: FilePath -> FilePath -> IO String
catFiles file1 file2 =
  do s1 <- readFile file1
     s2 <- readFile file2
     return (s1++s2)

-- `doTwice i` performs the instructions `i` twice
-- (or rather, produces a new instruction list, in which `i` is performed twice)
doTwice :: IO a -> IO (a,a)
doTwice io =
  do x <- io
     y <- io
     return (x,y)

-- `dont i` ignores the instructions `i` and does not perform any instructions
-- (or rather, produces an empty instruction list)
dont :: IO a -> IO ()
dont io =
  do return ()
  -- The do keyword is not needed here...

-- What does this code do?
example :: IO ()
example = dont (writeFile "file.txt" "this is a file")

-- `second is` performs the second set of instructions in `is`
-- (or rather, picks out the second set of instructions and ignores the rest)
second :: [IO a] -> IO a
second (_:io:_) = io
  -- This example shows that tnstructions are normal data! This means that
  -- instructions can be stored in data structures such as lists.

-- `sortFile f1 f2` reads the content of file `f1`, sorts its lines and writes
-- the result to file `f2`
sortFile :: FilePath -> FilePath -> IO ()
sortFile file1 file2 =
  do s <- readFile file1
     writeFile file2 (unlines (sort (lines s)))

-- myGetLine (a.k.a. `getLine`)
myGetLine :: IO String
myGetLine =
  do c <- getChar
     if c == '\n'
       then return ""
       else do cs <- myGetLine
               return (c:cs)
  -- This example shows that instruction lists can depend on the environment
  -- (in this case the user's input), and they can be built recursively.

-- `mySequence_ is` (a.k.a. `sequence`) takes a list of instruction sets and
-- performs them in sequence
-- (or rather, concatenates a list of instruction sets to a single instruction
-- set)
mySequence_ :: [IO ()] -> IO ()
mySequence_ [] = return ()
mySequence_ (io:ios) =
  do io
     mySequence_ ios

-- `mySequence` (a.k.a. `sequence`) is like `mySequence_`, but it also returns
-- all intermediate results as a list
mySequence :: [IO a] -> IO [a]
mySequence [] = return []
mySequence (io:ios) =
  do a  <- io
     as <- mySequence ios
     return (a:as)

-- `writeFiles f [s1,s2 ... sx]` produces the files `f++"1"`, `f++"2"` ...
-- `f++"x"` with the contents s1, s2 ... sx respectively
writeFiles :: FilePath -> [String] -> IO ()
writeFiles file xs =
  sequence_ [ writeFile (file++show i) x
            | (x,i) <- zip xs [1..]
            ]
  -- [1..] is an infinite list, but zip stops when xs ends

-- prog asks the user for a file name and some content and produces a file with
-- that name and content
prog :: IO ()
prog = do
    putStrLn "Which file?"
    file <- myGetLine
    putStrLn "What content?"
    contents <- myGetLine
    writeFile file contents
    putStrLn "Done."

main = prog
  -- Compile this file using
  --
  --     ghc --make ExampleIO.hs
  --
  -- That produces an executable file named ExampleIO which will perform the
  -- instructions given by `prog`.
  --
  -- See also <http://www.cse.chalmers.se/edu/course/TDA555/FAQ.html#compilation>.