{-# LANGUAGE TemplateHaskell #-}

-- | Template Haskell utils for declaring flags instances.
module Data.Flags.TH (
    dataBitsAsFlags,
    dataBitsAsBoundedFlags
  ) where

import Language.Haskell.TH

import Data.Bits (Bits(..))
import Control.Applicative ((<$>))

inst :: String -> Name -> [Dec] -> Dec
inst name typeName = InstanceD [] (AppT (ConT $ mkName name) (ConT typeName))

fun :: String -> Exp -> Dec
fun name expr = FunD (mkName name) [Clause [] (NormalB expr) []]

-- | Produces a 'Data.Flags.Flags' instance declaration for the specified
--   instance of 'Data.Bits.Bits'.
dataBitsAsFlags :: Name -> Q [Dec]
dataBitsAsFlags typeName = do
  noneE <- [| fromInteger 0 |]
  unionE <- [| (.|.) |]
  intersectionE <- [| (.&.) |] 
  differenceE <- [| \x -> \y -> x .&. (complement y) |]
  return [inst "Flags" typeName
            [fun "noFlags" noneE,
             fun "andFlags" unionE,
             fun "commonFlags" intersectionE,
             fun "butFlags" differenceE]]

-- | Produces 'Data.Flags.Flags' and 'Data.Flags.BoundedFlags' instances
--   declarations for the specified instance of 'Data.Bits.Bits'.
dataBitsAsBoundedFlags :: Name -> Q [Dec]
dataBitsAsBoundedFlags typeName = do
  allE <- [| fromInteger (-1) |]
  enumE <- [| \x -> map (setBit 0) $ filter (testBit x) [0 .. bitSize x - 1] |]
  (++ [inst "BoundedFlags" typeName
         [fun "allFlags" allE,
          fun "enumFlags" enumE]]) <$> dataBitsAsFlags typeName