content: {-# LANGUAGE LambdaCase #-}
  
  module Lib
    ( fromBirbs
    , fromTerm
    ) where
  
  import           Data.Bifunctor                 ( first
                                                  , second
                                                  )
  import           Data.Char                      ( digitToInt
                                                  , isDigit
                                                  )
  import           Data.Map                       ( Map )
  import qualified Data.Map                      as Map
  import           Term
  import           Utils
  
  type Birb = Char
  
  birbify :: Term -> Map Term Birb -> String
  birbify t m | t `Map.member` m = [Map.findWithDefault invalid t m]
              | (Abs a) <- t     = "[" ++ birbify a m ++ "]"
              | (App l r) <- t   = "(" ++ birbify l m ++ " " ++ birbify r m ++ ")"
              | (Idx i) <- t     = show i
  
  termify :: Map Birb Term -> String -> Term
  termify m s = foldlr (odd $ length lst) lst
   where
    go [c     ] = [Map.findWithDefault invalid c m]
    go (c : cs) = go [c] ++ go cs
    go _        = invalid
    lst = go s
  
    -- TODO: Rewrite so last/init aren't needed
    foldlr :: Bool -> [Term] -> Term
    foldlr _     [x]      = x
    foldlr _     [x, y]   = App x y
    foldlr True  xs       = let t = foldlr False (init xs) in App t (last xs)
    foldlr False (x : xs) = let t = foldlr True xs in App x t
    foldlr _     _        = invalid
  
  fromBirbs :: String -> Term
  fromBirbs s =
    let birbsies = Map.fromList $ second parse <$> birbs
        filtered = filter (`Map.member` birbsies) s
        term     = termify birbsies filtered
    in  term
  
  fromTerm :: Term -> String
  fromTerm t =
    let flipped  = (\(a, b) -> (b, a)) <$> birbs
        termsies = Map.fromList $ first parse <$> flipped
    in  birbify t termsies
  
  -- this isn't really relevant but I'm too lazy to type the terms manually
  parse :: String -> Term
  parse = fst . go
   where
    go ('!' : cs) = let (t, cs') = go cs in (Abs t, cs')
    go ('@' : cs) =
      let (l, cs' ) = go cs
          (r, cs'') = go cs'
      in  (App l r, cs'')
    go (i : cs) | isDigit i = (Idx $ digitToInt i, cs)
    go _                    = invalid
  
  birbs :: [(Birb, String)]
  birbs =
    [ ('\x1F426', "!0") -- bird
    , ('\x1F54A', "!!!!@@32@10") -- dove
    , ('\x1F424', "!!@0@@110") -- chick
    , ('\x1F425', "!!1") -- front chick
    , ('\x1F423', "!!!@0@21") -- hatching chick
    , ('\x1F989', "!!@0@10") -- owl
    , ('\x1F986', "!!!@0@12") -- duck
    , ('\x1F9A4', "!@!@1@00!@1@00") -- dodo
    , ('\x1F9A9', "!!!@@201") -- flamingo
    , ('\x1F9A2', "!!!@@20@10") -- swan
    , ('\x1FABD', "!!!!@@3@20@10") -- wing
    , ('\x1F99A', "!!!@1@20") -- peacock
    , ('\x1F99C', "!@00") -- parrot
    , ('\x1F985', "!!!!!@@43@@210") -- eagle
    , ('\x1F427', "!!!@2@10") -- penguin
  -- , ('\x1FAB6', "") -- feather
  -- , ('\x1F413', "") -- rooster
  -- , ('\x1F414', "") -- chicken
  -- , ('\x1F983', "") -- turkey
    ]
  