module Discord.Internal.Types.RolePermissions
  ( PermissionFlag (..),
    hasRolePermissions,
    hasRolePermission,
    newRolePermissions,
    newRolePermission,
    setRolePermissions,
    setRolePermission,
    clearRolePermissions,
    clearRolePermission,
    hasGuildMemberPermission,
  )
where

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 (PermissionFlag -> PermissionFlag -> Bool
(PermissionFlag -> PermissionFlag -> Bool)
-> (PermissionFlag -> PermissionFlag -> Bool) -> Eq PermissionFlag
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PermissionFlag -> PermissionFlag -> Bool
== :: PermissionFlag -> PermissionFlag -> Bool
$c/= :: PermissionFlag -> PermissionFlag -> Bool
/= :: PermissionFlag -> PermissionFlag -> Bool
Eq, Eq PermissionFlag
Eq PermissionFlag =>
(PermissionFlag -> PermissionFlag -> Ordering)
-> (PermissionFlag -> PermissionFlag -> Bool)
-> (PermissionFlag -> PermissionFlag -> Bool)
-> (PermissionFlag -> PermissionFlag -> Bool)
-> (PermissionFlag -> PermissionFlag -> Bool)
-> (PermissionFlag -> PermissionFlag -> PermissionFlag)
-> (PermissionFlag -> PermissionFlag -> PermissionFlag)
-> Ord PermissionFlag
PermissionFlag -> PermissionFlag -> Bool
PermissionFlag -> PermissionFlag -> Ordering
PermissionFlag -> PermissionFlag -> PermissionFlag
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: PermissionFlag -> PermissionFlag -> Ordering
compare :: PermissionFlag -> PermissionFlag -> Ordering
$c< :: PermissionFlag -> PermissionFlag -> Bool
< :: PermissionFlag -> PermissionFlag -> Bool
$c<= :: PermissionFlag -> PermissionFlag -> Bool
<= :: PermissionFlag -> PermissionFlag -> Bool
$c> :: PermissionFlag -> PermissionFlag -> Bool
> :: PermissionFlag -> PermissionFlag -> Bool
$c>= :: PermissionFlag -> PermissionFlag -> Bool
>= :: PermissionFlag -> PermissionFlag -> Bool
$cmax :: PermissionFlag -> PermissionFlag -> PermissionFlag
max :: PermissionFlag -> PermissionFlag -> PermissionFlag
$cmin :: PermissionFlag -> PermissionFlag -> PermissionFlag
min :: PermissionFlag -> PermissionFlag -> PermissionFlag
Ord, Int -> PermissionFlag
PermissionFlag -> Int
PermissionFlag -> [PermissionFlag]
PermissionFlag -> PermissionFlag
PermissionFlag -> PermissionFlag -> [PermissionFlag]
PermissionFlag
-> PermissionFlag -> PermissionFlag -> [PermissionFlag]
(PermissionFlag -> PermissionFlag)
-> (PermissionFlag -> PermissionFlag)
-> (Int -> PermissionFlag)
-> (PermissionFlag -> Int)
-> (PermissionFlag -> [PermissionFlag])
-> (PermissionFlag -> PermissionFlag -> [PermissionFlag])
-> (PermissionFlag -> PermissionFlag -> [PermissionFlag])
-> (PermissionFlag
    -> PermissionFlag -> PermissionFlag -> [PermissionFlag])
-> Enum PermissionFlag
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
$csucc :: PermissionFlag -> PermissionFlag
succ :: PermissionFlag -> PermissionFlag
$cpred :: PermissionFlag -> PermissionFlag
pred :: PermissionFlag -> PermissionFlag
$ctoEnum :: Int -> PermissionFlag
toEnum :: Int -> PermissionFlag
$cfromEnum :: PermissionFlag -> Int
fromEnum :: PermissionFlag -> Int
$cenumFrom :: PermissionFlag -> [PermissionFlag]
enumFrom :: PermissionFlag -> [PermissionFlag]
$cenumFromThen :: PermissionFlag -> PermissionFlag -> [PermissionFlag]
enumFromThen :: PermissionFlag -> PermissionFlag -> [PermissionFlag]
$cenumFromTo :: PermissionFlag -> PermissionFlag -> [PermissionFlag]
enumFromTo :: PermissionFlag -> PermissionFlag -> [PermissionFlag]
$cenumFromThenTo :: PermissionFlag
-> PermissionFlag -> PermissionFlag -> [PermissionFlag]
enumFromThenTo :: PermissionFlag
-> PermissionFlag -> PermissionFlag -> [PermissionFlag]
Enum, Int -> PermissionFlag -> ShowS
[PermissionFlag] -> ShowS
PermissionFlag -> String
(Int -> PermissionFlag -> ShowS)
-> (PermissionFlag -> String)
-> ([PermissionFlag] -> ShowS)
-> Show PermissionFlag
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PermissionFlag -> ShowS
showsPrec :: Int -> PermissionFlag -> ShowS
$cshow :: PermissionFlag -> String
show :: PermissionFlag -> String
$cshowList :: [PermissionFlag] -> ShowS
showList :: [PermissionFlag] -> ShowS
Show)

permissionBits :: PermissionFlag -> RolePermissions
permissionBits :: PermissionFlag -> RolePermissions
permissionBits PermissionFlag
p = RolePermissions -> Int -> RolePermissions
forall a. Bits a => a -> Int -> a
shift (Integer -> RolePermissions
RolePermissions Integer
1) (PermissionFlag -> Int
forall a. Enum a => a -> Int
fromEnum PermissionFlag
p)

-- | Check if a given role has all the permissions
hasRolePermissions :: [PermissionFlag] -> RolePermissions -> Bool
hasRolePermissions :: [PermissionFlag] -> RolePermissions -> Bool
hasRolePermissions [PermissionFlag]
permissions RolePermissions
rolePermissions = RolePermissions -> RolePermissions -> RolePermissions
forall a. Bits a => a -> a -> a
(.&.) RolePermissions
combinedPermissions RolePermissions
rolePermissions RolePermissions -> RolePermissions -> Bool
forall a. Eq a => a -> a -> Bool
== RolePermissions
combinedPermissions
  where
    combinedPermissions :: RolePermissions
combinedPermissions = [PermissionFlag] -> RolePermissions
combinePermissions [PermissionFlag]
permissions

-- | Check if a given role has the permission
hasRolePermission :: PermissionFlag -> RolePermissions -> Bool
hasRolePermission :: PermissionFlag -> RolePermissions -> Bool
hasRolePermission PermissionFlag
p RolePermissions
r = RolePermissions -> Integer
getRolePermissions (PermissionFlag -> RolePermissions
permissionBits PermissionFlag
p RolePermissions -> RolePermissions -> RolePermissions
forall a. Bits a => a -> a -> a
.&. RolePermissions
r) Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
> Integer
0

-- | Replace a users rolePerms
--   with a complete new set of permissions
newRolePermissions :: [PermissionFlag] -> RolePermissions
newRolePermissions :: [PermissionFlag] -> RolePermissions
newRolePermissions = [PermissionFlag] -> RolePermissions
combinePermissions

-- | Get the RolePermissions of a single PermissionFlag
newRolePermission :: PermissionFlag -> RolePermissions
newRolePermission :: PermissionFlag -> RolePermissions
newRolePermission = PermissionFlag -> RolePermissions
permissionBits

-- | Update RolePermissions with new permissions
setRolePermissions :: [PermissionFlag] -> RolePermissions -> RolePermissions
setRolePermissions :: [PermissionFlag] -> RolePermissions -> RolePermissions
setRolePermissions [PermissionFlag]
p RolePermissions
r = [PermissionFlag] -> RolePermissions
combinePermissions [PermissionFlag]
p RolePermissions -> RolePermissions -> RolePermissions
forall a. Bits a => a -> a -> a
.|. RolePermissions
r

-- | Unset Permissions from RolePermissions
clearRolePermissions :: [PermissionFlag] -> RolePermissions -> RolePermissions
clearRolePermissions :: [PermissionFlag] -> RolePermissions -> RolePermissions
clearRolePermissions [PermissionFlag]
p RolePermissions
r = (RolePermissions -> RolePermissions
forall a. Bits a => a -> a
complement (RolePermissions -> RolePermissions)
-> ([PermissionFlag] -> RolePermissions)
-> [PermissionFlag]
-> RolePermissions
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [PermissionFlag] -> RolePermissions
combinePermissions) [PermissionFlag]
p RolePermissions -> RolePermissions -> RolePermissions
forall a. Bits a => a -> a -> a
.&. RolePermissions
r

-- | Set a certain permission flag
--   This method doesn't lose the other already present permissions
setRolePermission :: PermissionFlag -> RolePermissions -> RolePermissions
setRolePermission :: PermissionFlag -> RolePermissions -> RolePermissions
setRolePermission PermissionFlag
p = RolePermissions -> RolePermissions -> RolePermissions
forall a. Bits a => a -> a -> a
(.|.) (PermissionFlag -> RolePermissions
permissionBits PermissionFlag
p)

-- | Remove a permission from a user by clearing the bit
clearRolePermission :: PermissionFlag -> RolePermissions -> RolePermissions
clearRolePermission :: PermissionFlag -> RolePermissions -> RolePermissions
clearRolePermission PermissionFlag
p = RolePermissions -> RolePermissions -> RolePermissions
forall a. Bits a => a -> a -> a
(.&.) (RolePermissions -> RolePermissions
forall a. Bits a => a -> a
complement (RolePermissions -> RolePermissions)
-> (PermissionFlag -> RolePermissions)
-> PermissionFlag
-> RolePermissions
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PermissionFlag -> RolePermissions
permissionBits (PermissionFlag -> RolePermissions)
-> PermissionFlag -> RolePermissions
forall a b. (a -> b) -> a -> b
$ PermissionFlag
p)

combinePermissions :: [PermissionFlag] -> RolePermissions
combinePermissions :: [PermissionFlag] -> RolePermissions
combinePermissions = (RolePermissions -> PermissionFlag -> RolePermissions)
-> RolePermissions -> [PermissionFlag] -> RolePermissions
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (\RolePermissions
rp -> (RolePermissions
rp RolePermissions -> RolePermissions -> RolePermissions
forall a. Bits a => a -> a -> a
.|.) (RolePermissions -> RolePermissions)
-> (PermissionFlag -> RolePermissions)
-> PermissionFlag
-> RolePermissions
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PermissionFlag -> RolePermissions
permissionBits) (Integer -> RolePermissions
RolePermissions Integer
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 :: Guild -> GuildMember -> PermissionFlag -> Bool
hasGuildMemberPermission Guild
g GuildMember
gm PermissionFlag
p = [RoleId] -> Bool
go (GuildMember -> [RoleId]
memberRoles GuildMember
gm)
  where
    go :: [RoleId] -> Bool
go [] = Bool
False
    go (RoleId
x : [RoleId]
xs) = case Guild -> RoleId -> Maybe Role
roleIdToRole Guild
g RoleId
x of
      Maybe Role
Nothing -> [RoleId] -> Bool
go [RoleId]
xs
      Just Role
a -> PermissionFlag
p PermissionFlag -> RolePermissions -> Bool
`hasRolePermission` Role -> RolePermissions
rolePerms Role
a Bool -> Bool -> Bool
|| [RoleId] -> Bool
go [RoleId]
xs