module Wordify.Rules.Dictionary (Dictionary, isValidWord, makeDictionary, invalidWords, dictionaryFromWords) where

  import qualified Data.HashSet as HashSet
  import Wordify.Rules.ScrabbleError
  import qualified Control.Exception as Exc
  import Text.ParserCombinators.Parsec
  import Data.Char
  import Control.Monad

  data Dictionary = Dictionary (HashSet.HashSet String) deriving Show

  {- | 
     Returns the list of words which are not present in the given dictionary from a 
     list of input words.  
  -}
  invalidWords :: Dictionary -> [String] -> [String]
  invalidWords dictionary = filter $ not . isValidWord dictionary

  {-
     Returns true if the given word is in the given dictionary.
  -}
  isValidWord :: Dictionary -> String -> Bool
  isValidWord (Dictionary dictionaryWords) = flip HashSet.member dictionaryWords

  dictionaryFromWords :: [String] -> Dictionary
  dictionaryFromWords wordList = Dictionary $ HashSet.fromList upperCaseWords
    where
      upperCaseWords = (map . map) toUpper wordList

  {- |
    Creates a dictionary from a file containing a list of valid words, each word being seperated by a newline.
  -}
  makeDictionary :: FilePath -> IO (Either ScrabbleError Dictionary)
  makeDictionary filePath = 
    do
      fileContents <- Exc.try (readFile filePath) :: IO (Either Exc.IOException String)
      case fileContents of 
        Left _ -> return $ Left (DictionaryFileNotFound filePath)
        Right dictContents -> 
              let dictWords = parseFile dictContents
              in case dictWords of 
                Left _ -> return $ Left (MalformedDictionaryFile filePath)
                Right wordList -> return $ Right (Dictionary $ HashSet.fromList wordList)

    where
      toUpperCase = (map . map) toUpper       
      parseFile contents = liftM toUpperCase $ parse dictionaryFile "Malformed dictionary file " contents

      dictionaryFile = 
        do
          dictWords <- many word
          _ <- eof
          return dictWords

      word = 
        do
          entry <- many letter
          _ <- newline
          return entry