-- | Binary decoding of a single term (without header)
getTerm :: Get Term
getTerm = do
  tag <- get8u
  case tag of
    97  -> IntTerm <$> get8u
    98  -> IntTerm <$> get32s
    99  -> getL 31 >>= return . FloatTerm . read . C.unpack
    100 -> get16u >>= getL >>= return . AtomTerm . C.unpack
    104 -> get8u >>= getN >>= tupleTerm
    105 -> get32u >>= getN >>= tupleTerm
    106 -> return $ ListTerm []
    107 -> get16u >>= getL >>= return . BytelistTerm
    108 -> get32u >>= \n -> getN n <* expectNil >>= return . ListTerm
    109 -> get32u >>= getL >>= return . BinaryTerm
    110 -> getBigint get8u >>= return . BigintTerm . fromIntegral
    111 -> getBigint get32u >>= return . BigintTerm . fromIntegral
  where
    getN :: Int -> Get [Term]
    getN n = replicateM n getTerm
    expectNil :: Get ()
    expectNil = do
      tag <- get8u
      case tag of
        106 -> return ()
        _   -> fail $ "invalid list - expected list ending with Nil"
    -- First try & decode composite terms.
    tupleTerm [AtomTerm "bert", AtomTerm "true"]  = return $ BoolTerm True
    tupleTerm [AtomTerm "bert", AtomTerm "false"] = return $ BoolTerm False
    tupleTerm [AtomTerm "bert", AtomTerm "dict", ListTerm kvs] =
      mapM toTuple kvs >>= return . DictionaryTerm
      where
        toTuple (TupleTerm [k, v]) = return $ (k, v)
        toTuple _ = fail "invalid dictionary"
    tupleTerm [AtomTerm "bert", AtomTerm "time",
               IntTerm mS, IntTerm s, IntTerm uS] =
      return $ TimeTerm $ composeTime (mS, s, uS)
    tupleTerm [AtomTerm "bert", AtomTerm "regex",
               BytelistTerm s, ListTerm os] =
      options os >>= return . RegexTerm (C.unpack s)
      where
        -- TODO: type-check the options values as well
        options []                = return []
        options ((AtomTerm o):os) = options os >>= return . (o:)
        options _                 = fail "regex options must be atoms"
    -- All other tuples are just .. tuples
    tupleTerm xs = return $ TupleTerm xs
getTerm :: Get Term
getTerm = do
  tag <- get8u
  case tag of
    97  -> IntTerm <$> get8u
    98  -> IntTerm <$> get32s
    99  -> getL 31 >>= return . FloatTerm . read . C.unpack
    100 -> get16u >>= getL >>= return . AtomTerm . C.unpack
    104 -> get8u >>= getN >>= tupleTerm
    105 -> get32u >>= getN >>= tupleTerm
    106 -> return $ ListTerm []
    107 -> get16u >>= getL >>= return . BytelistTerm
    108 -> get32u >>= \n -> getN n <* expectNil >>= return . ListTerm
    109 -> get32u >>= getL >>= return . BinaryTerm
    110 -> getBigint get8u >>= return . BigintTerm . fromIntegral
    111 -> getBigint get32u >>= return . BigintTerm . fromIntegral
  where
    getN :: Int -> Get [Term]
    getN n = replicateM n getTerm
    expectNil :: Get ()
    expectNil = do
      tag <- get8u
      case tag of
        106 -> return ()
        _   -> fail $ "invalid list - expected list ending with Nil"
    -- First try & decode composite terms.
    tupleTerm [AtomTerm "bert", AtomTerm "true"]  = return $ BoolTerm True
    tupleTerm [AtomTerm "bert", AtomTerm "false"] = return $ BoolTerm False
    tupleTerm [AtomTerm "bert", AtomTerm "dict", ListTerm kvs] =
      mapM toTuple kvs >>= return . DictionaryTerm
      where
        toTuple (TupleTerm [k, v]) = return $ (k, v)
        toTuple _ = fail "invalid dictionary"
    tupleTerm [AtomTerm "bert", AtomTerm "time",
               IntTerm mS, IntTerm s, IntTerm uS] =
      return $ TimeTerm $ composeTime (mS, s, uS)
    tupleTerm [AtomTerm "bert", AtomTerm "regex",
               BytelistTerm s, ListTerm os] =
      options os >>= return . RegexTerm (C.unpack s)
      where
        -- TODO: type-check the options values as well
        options []                = return []
        options ((AtomTerm o):os) = options os >>= return . (o:)
        options _                 = fail "regex options must be atoms"
    -- All other tuples are just .. tuples
    tupleTerm xs = return $ TupleTerm xs
getTerm = do
  tag <- get8u
  case tag of
    97  -> IntTerm <$> get8u
    98  -> IntTerm <$> get32s
    99  -> getL 31 >>= return . FloatTerm . read . C.unpack
    100 -> get16u >>= getL >>= return . AtomTerm . C.unpack
    104 -> get8u >>= getN >>= tupleTerm
    105 -> get32u >>= getN >>= tupleTerm
    106 -> return $ ListTerm []
    107 -> get16u >>= getL >>= return . BytelistTerm
    108 -> get32u >>= \n -> getN n <* expectNil >>= return . ListTerm
    109 -> get32u >>= getL >>= return . BinaryTerm
    110 -> getBigint get8u >>= return . BigintTerm . fromIntegral
    111 -> getBigint get32u >>= return . BigintTerm . fromIntegral
  where
    getN :: Int -> Get [Term]
    getN n = replicateM n getTerm
    expectNil :: Get ()
    expectNil = do
      tag <- get8u
      case tag of
        106 -> return ()
        _   -> fail $ "invalid list - expected list ending with Nil"
    -- First try & decode composite terms.
    tupleTerm [AtomTerm "bert", AtomTerm "true"]  = return $ BoolTerm True
    tupleTerm [AtomTerm "bert", AtomTerm "false"] = return $ BoolTerm False
    tupleTerm [AtomTerm "bert", AtomTerm "dict", ListTerm kvs] =
      mapM toTuple kvs >>= return . DictionaryTerm
      where
        toTuple (TupleTerm [k, v]) = return $ (k, v)
        toTuple _ = fail "invalid dictionary"
    tupleTerm [AtomTerm "bert", AtomTerm "time",
               IntTerm mS, IntTerm s, IntTerm uS] =
      return $ TimeTerm $ composeTime (mS, s, uS)
    tupleTerm [AtomTerm "bert", AtomTerm "regex",
               BytelistTerm s, ListTerm os] =
      options os >>= return . RegexTerm (C.unpack s)
      where
        -- TODO: type-check the options values as well
        options []                = return []
        options ((AtomTerm o):os) = options os >>= return . (o:)
        options _                 = fail "regex options must be atoms"
    -- All other tuples are just .. tuples
    tupleTerm xs = return $ TupleTerm xs