import Data.List.Split
import Data.List
import qualified Data.Map.Strict as M

main :: IO ()
main = do
  input <- map (map (map sort . words) . splitOn "|") <$> lines <$> readFile "day08/input"
  let output = map (!!1) input
  print $ length $ filter (`elem` [2,3,4,7]) $ map length (concat output)
  print $ day08 input

findDict :: [String] -> M.Map Char Char
findDict wire = M.fromList $ zip (a++b++c++d++e++f++g) "abcdefg"
    where a = intersect (getX 3) (without [2,4])
          b = intersect (charsWithLength 3 6) (charsWithLength 1 5)
          c = intersect (charsWithLength 2 6) (getX 2)
          d = intersect (charsWithLength 2 6) (charsWithLength 3 5)
          e = intersect (charsWithLength 1 5) $ intersect (getX 7) $ without [2..4]
          f = getX 2 \\ c
          g = without [2..4] \\ e
          without x = "abcdefg" \\ (concat $ filter (flip elem x . length) wire)
          getXs x = filter (\y->length y == x) wire
          getX = head . getXs
          charsWithLength x c = nub $ concat $ filter (\y->length y == x) $ group $ sort $ concat $ getXs c

decode :: M.Map Char Char -> String -> String
decode _ [] = []
decode dict (x:xs) = dict M.! x : decode dict xs

findNum :: String -> Int
findNum x
  | x=="abcefg" = 0
  | x=="cf" = 1
  | x=="acdeg" = 2
  | x=="acdfg" = 3
  | x=="bcdf" = 4
  | x=="abdfg" = 5
  | x=="abdefg" = 6
  | x=="acf" = 7
  | x=="abcdefg" = 8
  | x=="abcdfg" = 9

day08 :: [[[String]]] -> Int
day08 [] = 0
day08 ((wire:output):xs) = day08 xs + (sum $ zipWith (*) decoded [10^x|x<-[0..]])
    where dict = findDict wire
          decoded = reverse $ map (findNum . sort . decode dict) (head output)
