{-# LANGUAGE  ViewPatterns, ScopedTypeVariables #-}

module Data.Binary.Enum where

import Prelude
import Data.Binary
import Control.Applicative
import qualified Data.List as DL

-- | we can store Enum values inside values we know how to store, so let
newtype BitMap t a = BitMap [a]
  -- ^ Map lets you encode 'flags' compressing distinct eg. packing 8 diferent flags per byte
newtype BitEnc t a = BitEnc a
          -- ^ t is a store type, you will need to make sure thats your enum fits there

instance (Enum a, Enum t, Binary t) => Binary (BitEnc t a) where
  put (BitEnc a) = put (toEnum . fromEnum $ a :: t)
  get = BitEnc . toEnum . fromEnum <$> (get :: Get t)

instance (Enum a, Eq a, Eq t, Num t, Integral t, Binary t) => Binary (BitMap t a) where
  put (BitMap a) = put ( foldr (\(fromEnum -> x) y -> 2 ^ x + y ) 0 (DL.nub a) :: t )
  get = BitMap . process 0 <$> (get :: Get t)
    where
      process _ 0    = []
      process k (flip divMod 2 -> (d, m))
        | m == 1     = toEnum k : process (k + 1) d
        | otherwise  =            process (k + 1) d