import Data.List

type Name = String

type Size = Int

data Inode
  = File Name Size
  | Folder Name [Inode]
  deriving (Show)

data ICrumb =
  ICrumb Name [Inode]
  deriving (Show)

type IZipper = (Inode, [ICrumb])

main :: IO ()
main = do
  input <- map words . lines <$> readFile "input"
  let fullFS = walk (tail input) (Folder "/" [], [])
  let sizeList = getDirSizeList $ upTop fullFS
  print $ sum $ filter (< 100000) sizeList
  let toDelete = subtract 40000000 $ getDirSize $ upTop fullFS
  print $ head $ dropWhile (< toDelete) $ sort sizeList

up :: IZipper -> IZipper
up (node, ICrumb name nodes:crumbs) = (Folder name (node : nodes), crumbs)

upTop :: IZipper -> IZipper
upTop (inode, []) = (inode, [])
upTop z = upTop (up z)

newFile :: Inode -> IZipper -> IZipper
newFile node (Folder name nodes, crumbs) = (Folder name (node : nodes), crumbs)

goTo :: Name -> IZipper -> IZipper
goTo name (Folder fname nodes, crumbs) =
  let (ls, node:rs) = break (nameIs name) nodes
   in (node, ICrumb fname (ls ++ rs) : crumbs)

nameIs :: Name -> Inode -> Bool
nameIs name (Folder folderName _) = name == folderName
nameIs name (File fileName _) = name == fileName

walk :: [[String]] -> IZipper -> IZipper
walk (["$", "cd", name]:xs) z =
  walk xs $
  if name == ".."
    then up z
    else goTo name z
walk (["$", "ls"]:xs) z = walk xs z
walk (["dir", name]:xs) z = walk xs $ newFile (Folder name []) z
walk ([size, name]:xs) z = walk xs $ newFile (File name (read size)) z
walk _ z = z

getDirSize :: IZipper -> Int
getDirSize (Folder _ nodes, _) = sum $ map getSize nodes
  where
    getSize (File _ size) = size
    getSize folder = getDirSize (folder, [])
getDirSize _ = 0

getDirSizeList :: IZipper -> [Int]
getDirSizeList (Folder _ nodes, _) =
  getDirSize (Folder "" nodes, []) :
  concat [getDirSizeList (node, []) | node <- nodes]
getDirSizeList _ = []
