module Data.FlagSet.PackedRecord (
   getIntByMask, putIntByMask, accessorIntByMask,
   getIntByRange, putIntByRange, accessorIntByRange,
   ) where

import Data.FlagSet (T(Cons), Mask(Mask), )

import qualified Data.Bits as B
import Data.Bits (Bits, (.&.), (.|.), )

import qualified Data.Accessor.Basic as Acc

import Data.EnumSet.Utility ((.-.), )


leastSigBit :: (Num w, Bits w) => w -> w
leastSigBit m = (-m) .&. m

getIntByMask ::
   (Bits w, Integral w, Integral i) =>
   Mask w a b -> T w a -> i
getIntByMask (Mask m) (Cons fs) =
   -- I hope that the division is converted to a shift
   fromIntegral $ div (m .&. fs) (leastSigBit m)

putIntByMask ::
   (Bits w, Integral w, Integral i) =>
   Mask w a b -> i -> T w a -> T w a
putIntByMask (Mask m) i (Cons fs) =
   Cons $ (fs .-. m) .|. (fromIntegral i * leastSigBit m)

accessorIntByMask ::
   (Bits w, Integral w, Integral i) =>
   Mask w a b -> Acc.T (T w a) i
accessorIntByMask m =
   Acc.fromSetGet (putIntByMask m) (getIntByMask m)


maskFromNumber ::
   (Num w, Bits w) =>
   Int -> w
maskFromNumber number =
   B.shiftL 1 number - 1

getIntByRange ::
   (Bits w, Integral w, Integral i) =>
   Int -> Int -> T w a -> i
getIntByRange number start (Cons fs) =
   fromIntegral $ B.shiftR fs start .&. maskFromNumber number

putIntByRange ::
   (Bits w, Integral w, Integral i) =>
   Int -> Int -> i -> T w a -> T w a
putIntByRange number start i (Cons fs) =
   Cons $
      (fs .-. B.shiftL (maskFromNumber number) start)
      .|.
      B.shiftL (fromIntegral i) start

accessorIntByRange ::
   (Bits w, Integral w, Integral i) =>
   Int -> Int -> Acc.T (T w a) i
accessorIntByRange number start =
   Acc.fromSetGet (putIntByRange number start) (getIntByRange number start)