content: module Discord.Internal.Types.RolePermissions
    ( PermissionFlag (..),
      hasRolePermissions,
      hasRolePermission,
      newRolePermissions,
      newRolePermission,
      setRolePermissions,
      setRolePermission,
      clearRolePermissions,
      clearRolePermission,
      hasGuildMemberPermission,
    )
  where
  
  import Prelude hiding (Foldable(..))
  import Data.Bits (Bits (complement, shift, (.&.), (.|.)))
  import Discord.Internal.Types.Guild
    ( Guild,
      Role (rolePerms),
      roleIdToRole,
    )
  import Discord.Internal.Types.Prelude (RolePermissions (..))
  import Discord.Internal.Types.User (GuildMember (memberRoles))
  import Data.Foldable (foldl')
  
  data PermissionFlag
    = CREATE_INSTANT_INVITE
    | KICK_MEMBERS
    | BAN_MEMBERS
    | ADMINISTRATOR
    | MANAGE_CHANNELS
    | MANAGE_GUILD
    | ADD_REACTIONS
    | VIEW_AUDIT_LOG
    | PRIORITY_SPEAKER
    | STREAM
    | VIEW_CHANNEL
    | SEND_MESSAGES
    | SEND_TTS_MESSAGES
    | MANAGE_MESSAGES
    | EMBED_LINKS
    | ATTACH_FILES
    | READ_MESSAGE_HISTORY
    | MENTION_EVERYONE
    | USE_EXTERNAL_EMOJIS
    | VIEW_GUILD_INSIGHT
    | CONNECT
    | SPEAK
    | MUTE_MEMBERS
    | DEAFEN_MEMBERS
    | MOVE_MEMBERS
    | USE_VAD
    | CHANGE_NICKNAME
    | MANAGE_NICKNAMES
    | MANAGE_ROLES
    | MANAGE_WEBHOOKS
    | MANAGE_EMOJIS_AND_STICKERS
    | USE_APPLICATION_COMMANDS
    | REQUEST_TO_SPEAK
    | MANAGE_EVENTS
    | MANAGE_THREADS
    | CREATE_PUBLIC_THREADS
    | CREATE_PRIVATE_THREADS
    | USE_EXTERNAL_STICKERS
    | SEND_MESSAGES_IN_THREADS
    | USE_EMBEDDED_ACTIVITIES
    | MODERATE_MEMBERS
    deriving (Eq, Ord, Enum, Show)
  
  permissionBits :: PermissionFlag -> RolePermissions
  permissionBits p = shift (RolePermissions 1) (fromEnum p)
  
  -- | Check if a given role has all the permissions
  hasRolePermissions :: [PermissionFlag] -> RolePermissions -> Bool
  hasRolePermissions permissions rolePermissions = (.&.) combinedPermissions rolePermissions == combinedPermissions
    where
      combinedPermissions = combinePermissions permissions
  
  -- | Check if a given role has the permission
  hasRolePermission :: PermissionFlag -> RolePermissions -> Bool
  hasRolePermission p r = getRolePermissions (permissionBits p .&. r) > 0
  
  -- | Replace a users rolePerms
  --   with a complete new set of permissions
  newRolePermissions :: [PermissionFlag] -> RolePermissions
  newRolePermissions = combinePermissions
  
  -- | Get the RolePermissions of a single PermissionFlag
  newRolePermission :: PermissionFlag -> RolePermissions
  newRolePermission = permissionBits
  
  -- | Update RolePermissions with new permissions
  setRolePermissions :: [PermissionFlag] -> RolePermissions -> RolePermissions
  setRolePermissions p r = combinePermissions p .|. r
  
  -- | Unset Permissions from RolePermissions
  clearRolePermissions :: [PermissionFlag] -> RolePermissions -> RolePermissions
  clearRolePermissions p r = (complement . combinePermissions) p .&. r
  
  -- | Set a certain permission flag
  --   This method doesn't lose the other already present permissions
  setRolePermission :: PermissionFlag -> RolePermissions -> RolePermissions
  setRolePermission p = (.|.) (permissionBits p)
  
  -- | Remove a permission from a user by clearing the bit
  clearRolePermission :: PermissionFlag -> RolePermissions -> RolePermissions
  clearRolePermission p = (.&.) (complement . permissionBits $ p)
  
  combinePermissions :: [PermissionFlag] -> RolePermissions
  combinePermissions = foldl' (\rp -> (rp .|.) . permissionBits) (RolePermissions 0)
  
  -- | Check if any Role of an GuildMember has the needed permission
  --   If the result of roleIdToRole is Nothing, it prepends a "False"
  --   Otherwise it checks for the needed permission
  hasGuildMemberPermission :: Guild -> GuildMember -> PermissionFlag -> Bool
  hasGuildMemberPermission g gm p = go (memberRoles gm)
    where
      go [] = False
      go (x : xs) = case roleIdToRole g x of
        Nothing -> go xs
        Just a -> p `hasRolePermission` rolePerms a || go xs
  