module Guess where

import Control.Exception(catch, IOException)
import System.Random(randomRIO)
import Data.Char (isAlpha, toLower)
import System.Environment(getArgs)

-- Global constant: number of unsuccessful tries allowed.
numTries :: Int
numTries = 8

-- Utility function which picks a random element from a (non-empty) list
pickRandom :: [alpha]-> IO alpha
pickRandom [] = error "pickRandom: empty list"
pickRandom xs = do 
  i <- randomRIO (0, length xs - 1)
  return $ xs !! i

-- Show a string, but mask all characters *not* occuring in first argument
render :: String-> String-> String
render rev = map (\c-> if c `elem` rev then c else '_')

{-- guess --}
getGuess :: String-> String-> IO Char
{-- end --}
getGuess hits misses = do
  putStr $ "Your guess? "
  i<- getLine 
  case i of
    "?" -> do 
      putStrLn $ "Your misses so far: "++ filter isAlpha misses
      getGuess hits misses
    [c] | c `elem` misses -> do
      putStrLn "You already tried that."
      getGuess hits misses
    [c] -> return $ toLower c
    _ -> do
      putStrLn "Please, one guess at a time."
      getGuess hits misses


{-- play --}
play :: String-> String-> String-> IO ()
{-- end --}
play word hits misses
  | all (`elem` hits) word = do
      putStrLn $ "Excellent --- you guessed "++ word++ "."
  | length misses == numTries = do 
      putStrLn $ "The word is "++ word++ " -- you lose."
  | otherwise = do
      putStrLn (render hits word)
      putStrLn $ show (length misses) ++ " misses of "++ show numTries++ " tries."
      c <- getGuess hits misses
      if (not $ c `elem` word) then
        do putStrLn "Miss."; play word hits (c:misses)
        else play word (c:hits) misses

{-- main --}
main :: IO ()
{-- end --}
main = 
  catch (do 
    as <- getArgs
    let df= case as of 
                ["de"] -> "dict-de_DE"
                ["en"] -> "dict-en_EN" 
                _ -> "dict-de_DE"
    dict <- readFile df 
    putStrLn $ "Selected dictionary "++ df ++ " ("++ show (length (lines dict))++ " entries)"
    secret <- pickRandom (filter ((5 <). length) (lines dict))
    play (map toLower secret) "" "")
    (\e-> putStrLn $ "Error: "++ show (e:: IOException))