{-# LANGUAGE LambdaCase #-}
module Pretzel where

import qualified  Data.Map as Map
import            Data.Maybe (catMaybes)
import qualified  Data.Set as Set

type Cell = (Int, Int, Int)
type Pretzel = Set.Set Cell

showPretzel :: Pretzel -> String
showPretzel pretzel =
  let
    cells = Set.toList pretzel
    (xmin, xmax) = bounds [x | (x, _, _) <- cells]
    (ymin, ymax) = bounds [y | (_, y, _) <- cells]
    (zmin, zmax) = bounds [z | (_, _, z) <- cells]
    slices =
      [ unlines
          [ [ if (x, y, z) `Set.member` pretzel then '#' else '.'
            | x <- [xmin .. xmax]
            ]
          | y <- [ymin .. ymax]
          ]
      | z <- [zmin .. zmax]
      ]
  in
    unlines slices

readPretzel :: String -> Maybe Pretzel
readPretzel str =
  let
    cells = catMaybes
      [ if cell == '#' then Just (x, y, z) else Nothing
      | (z, slice) <- zip [0..] $ sliceWith ("" /=) $ lines str
      , (y, row) <- zip [0..] slice
      , (x, cell) <- zip [0..] row
      ]
  in
    if all (`elem` ".#\n") str
    then Just $ Set.fromList cells
    else Nothing

bounds :: (Ord a) => [a] -> (a, a)
bounds xs =
  (minimum xs, maximum xs)

sliceWith :: (a -> Bool) -> [a] -> [[a]]
sliceWith p = \case
  [] -> []
  xs ->
    let
      (accept, rest) = span p xs
      (_, xs') = span (not . p) rest
    in
      accept : sliceWith p xs'
