module DictUtil where

import Data.Set
import Data.Map
import Data.Char
import Data.List
import SquareDefs


blockedSq :: Char
blockedSq = 'X'

set_unwrap :: Maybe (Set [Square]) -> Set [Square] 
set_unwrap (Just x) = x
set_unwrap Nothing  = Data.Set.empty

getWordSet :: Map Int (Set [Square]) -> Int -> Set [Square]
getWordSet dict len = set_unwrap $ Data.Map.lookup len dict

pick_word :: Set [Square] -> [[Square]]
pick_word dict_slice = Data.Set.toList dict_slice

fill_word :: Map Int (Set [Square]) -> [Square] -> [[Square]]
fill_word _ [] = [[]]
fill_word dict single_word = pick_word candidateSet
                where candidateSet = getWordSet dict $ length single_word

fill_line :: Map Int (Set [Square]) -> [Square] -> [[Square]]
fill_line _ [] = [[]]
fill_line dict xs = [ chosen_word ++ blocked ++ chosenRest | chosen_word <- fill_word dict firstWord,
                                                             chosenRest <- fill_line dict restOfLine ]
                        where (firstWord, suffix) = break isBlocked xs
                              (blocked, restOfLine) = span isBlocked suffix
                              isBlocked Black = True
                              isBlocked (White _) = False


fill_crossword :: Map Int (Set [Square]) -> [[Square]] -> [[[Square]]]
fill_crossword _ [] = [[]]
fill_crossword dict (l:ls) = [ chosen_line : chosen_rest | chosen_rest <- fill_crossword dict ls,
                                                           chosen_line <- fill_line dict l ] 


                                        {- sequence result_grid
                                    where result_grid = Prelude.map (fill_line dict) unfilled_grid -}


check_word :: Map Int (Set [Square]) -> [Square] -> Bool
check_word _ [] = True
check_word dict single_word = Data.Set.member single_word candidateSet
                where candidateSet = getWordSet dict $ length single_word

check_line :: Map Int (Set [Square]) -> [Square] -> Bool
check_line _ [] = True
check_line dict xs = check_word dict firstWord && check_line dict restOfLine
                where (firstWord, suffix) = break isBlocked xs
                      (_, restOfLine) = span isBlocked suffix
                      isBlocked Black = True
                      isBlocked (White _) = False

check_crossword :: Map Int (Set [Square]) -> [[Square]] -> Bool
check_crossword dict grid = Prelude.foldl (&&) True (Prelude.map (check_line dict) grid)

check_crosswords :: Map Int (Set [Square]) -> [[[Square]]] -> Bool
check_crosswords dict grids = Prelude.foldl (&&) True (Prelude.map (check_crossword dict) grids)

valid_dict_word :: String -> Bool
valid_dict_word w = Prelude.foldl (&&) True $ Prelude.map isAcceptable w
                    where isAcceptable c = isAscii c && isLower c

get_valid_words :: String -> [String]
get_valid_words streamIn = Prelude.filter valid_dict_word $ words streamIn 

insert_word :: Map Int (Set [Square]) -> String -> Set [Square]
insert_word dict single_word = Data.Set.insert squarified_word len_set
                                where len_set = getWordSet dict $ length single_word
                                      squarified_word = Prelude.map toSquare single_word
                                      toSquare c = White c

update_set :: Map Int (Set [Square]) -> String -> Map Int (Set [Square])
update_set dict single_word = Data.Map.insert word_len updated_set dict
                              where word_len = length single_word
                                    updated_set = insert_word dict single_word

build_dict_rec :: Map Int (Set [Square]) -> [String] -> Map Int (Set [Square])
build_dict_rec dict [] = dict
build_dict_rec dict (w:ws)= update_set (build_dict_rec dict ws) w

build_dict :: String -> Map Int (Set [Square])
build_dict streamIn = build_dict_rec Data.Map.empty valid_words
                      where valid_words = get_valid_words streamIn

fill_and_check :: Map Int (Set [Square]) -> [[Square]] -> [[[Square]]]
fill_and_check dict emptyGrid = Prelude.filter (check_crossword dict) candidates
                                where candidates = Prelude.map transpose $ fill_crossword dict emptyGrid
