{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}

-- | (More than just) Van Laarhoven lenses for XMPP types. The accessors in here
-- are designed to work with an optics library like lens or lens-family. This
-- module also provides a few simple functions ('view', 'modify', 'set' and
-- 'getAll') so you don't need to pull in another library to get some use out
-- of them.
--
-- * The name of the lenses corresponds to the field name of the data types with
-- an upper-case L appended. For documentation of the fields refer to the documentation of the data types (linked in the section header)
--
-- * Same goes for Traversals, except they are suffixed with a \'T\'
--
-- * Prism generally start with an underscore
--
-- /NB/ you do not need to import this module to get access to the optics
-- defined herein. They are also exported from Network.Xmpp. You only need to
-- import this module if you want to use the complementary accessor functions
-- without using an optics library like lens or lens-family

module Network.Xmpp.Lens
       ( Lens
       , Traversal
       , Prism
       , Iso
         -- * Accessors
         -- | Reimplementation of the basic lens functions so you don't have to
         -- bring in a lens library to use the optics

         -- ** Lenses
       , LF.view
       , modify
       , LF.set
         -- * Traversals
       , getAll
         -- * Prisms

         -- ** Construction
       , prism'
       , mkLens
       , mkIso
         -- * Lenses

         -- ** JID
       , _JidText
       , _isFull
       , _isBare

         -- ** Stanzas and Nonzas
       , _Stanza
       , _Nonza
       , _IQRequest
       , _IQResult
       , _IQError
       , _Message
       , _MessageError
       , _Presence
       , _PresenceError
       , IsStanza(..)
       , HasStanzaPayload(..)
       , IsErrorStanza(..)
       , messageTypeL
       , presenceTypeL
       , iqRequestTypeL
         -- *** 'StanzaError'
       , stanzaErrorTypeL
       , stanzaErrorConditionL
       , stanzaErrorTextL
       , stanzaErrorApplL
         -- ** Stream

         -- ** Stream Features
       , featureTlsL
       , featureMechanismsL
       , featureRosterVerL
       , featurePreApprovalL
       , featuresOtherL
         -- *** 'StreamConfiguration'
       , preferredLangL
       , toJidL
       , connectionDetailsL
       , resolvConfL
       , tlsBehaviourL
       , tlsParamsL
         -- **** TLS parameters
       , clientServerIdentificationL
       , tlsServerIdentificationL
       , clientSupportedL
       , supportedCiphersL
       , supportedVersionsL
       , tlsSupportedCiphersL
       , tlsSupportedVersionsL
       , clientUseServerNameIndicationL
       , tlsUseNameIndicationL
         -- *** 'SessionConfiguration'
       , streamConfigurationL
       , onConnectionClosedL
       , sessionStanzaIDsL
       , ensableRosterL
       , onRosterPushL
       , pluginsL
       , onPresenceChangeL
         -- ** IM
         -- *** Roster
         -- **** 'Roster'
       , verL
       , itemsL
         -- **** 'Item'
       , riApprovedL
       , riAskL
       , riJidL
       , riNameL
       , riSubscriptionL
       , riGroupsL
         -- **** 'QueryItem'
       , qiApprovedL
       , qiAskL
       , qiJidL
       , qiNameL
       , qiSubscriptionL
       , qiGroupsL
         -- **** 'Query'
       , queryVerL
       , queryItemsL
         -- ** IM Message
         -- *** 'MessageBody'
       , bodyLangL
       , bodyContentL
         -- *** 'MessageThread'
       , threadIdL
       , threadParentL
         -- *** 'MessageSubject'
       , subjectLangL
       , subjectContentL
         -- *** 'InstantMessage'
       , imThreadL
       , imSubjectL
       , imBodyL
         -- ** 'IMPresence'
       , showStatusL
       , statusL
       , priorityL

       )
       where

import           Control.Applicative
import qualified Data.ByteString as BS
import           Data.Functor.Identity (Identity(..))
import qualified Data.Map as Map
import           Data.Profunctor
import           Data.Text (Text)
import qualified Data.Text as Text
import           Data.XML.Types (Element)
import qualified Lens.Family2 as LF
import           Network.DNS (ResolvConf)
import           Network.TLS as TLS
import           Network.Xmpp.Concurrent.Types
import           Network.Xmpp.IM.Message
import           Network.Xmpp.IM.Presence
import           Network.Xmpp.IM.PresenceTracker.Types
import           Network.Xmpp.IM.Roster.Types
import           Network.Xmpp.Types

-- | Van-Laarhoven lenses.
{-# DEPRECATED Lens "Use Lens' from lens-family or lens" #-}
type Lens a b = forall f . Functor f => (b -> f b) -> a -> f a

{-# DEPRECATED Traversal "Use Traversal' from lens-family or lens" #-}
type Traversal a b = forall f . Applicative f => (b -> f b) -> a -> f a

type Prism a b = forall p f. (Choice p, Applicative f) => p b (f b) -> p a (f a)

type Iso a b = forall p f. (Profunctor p, Functor f) => p b (f b) -> p a (f a)

prism' :: (b -> s) -> (s -> Maybe b) -> Prism s b
prism' bs sma = dimap (\s -> maybe (Left s) Right (sma s))
                      (either pure (fmap bs)) . right'

mkLens :: (a -> b) -> (b -> a ->  a) -> Lens a b
mkLens get set = \inj x -> fmap (flip set x) (inj $ get x)

mkIso :: (a -> b) -> (b -> a) -> Iso a b
mkIso t f = dimap t (fmap f)

newtype Collect a b = Collect {getCollection :: [a]} deriving Functor

instance Applicative (Collect a) where
    pure _ = Collect []
    Collect xs <*> Collect ys = Collect $ xs ++ ys

{-# DEPRECATED getAll "use toListOf (lens-family), partsOf (lens) or similar" #-}
-- | Return all the values a Traversal is pointing to in a list
getAll :: Traversal a b -> a -> [b]
getAll t = getCollection . t (Collect . pure)

{-# DEPRECATED modify "use over (lens-family, lens)" #-}
modify :: Traversal a b -> (b -> b) -> a -> a
modify t f = runIdentity . t (Identity . f)

-- Xmpp Lenses
--------------------

_JidText :: Prism Text Jid
_JidText = prism' jidToText jidFromText

_isFull :: Prism Jid Jid
_isFull = prism' id (\j -> if isFull j then Just j else Nothing)

_isBare :: Prism Jid Jid
_isBare = prism' toBare (\j -> if isBare j then Just j else Nothing)

_Stanza :: Prism XmppElement Stanza
_Stanza = prism' XmppStanza (\v -> case v of
                                    XmppStanza s -> Just s
                                    _ -> Nothing)

_Nonza :: Prism XmppElement Element
_Nonza = prism' XmppNonza (\v -> case v of
                                  XmppNonza n -> Just n
                                  _ -> Nothing)


class IsStanza s where
    -- | From-attribute of the stanza
    from :: Lens s (Maybe Jid)
    -- | To-attribute of the stanza
    to   :: Lens s (Maybe Jid)
    -- | Langtag of the stanza
    lang :: Lens s (Maybe LangTag)
    -- | Stanza ID. Setting this to /Nothing/ for IQ* stanzas will set the id to
    -- the empty Text.
    sid :: Lens s (Maybe Text)
    -- | Traversal over the payload elements.
    payloadT :: Traversal s Element

traverseList :: Traversal [a] a
traverseList _inj [] = pure []
traverseList inj  (x:xs) = (:) <$> inj x <*> traverseList inj xs

instance IsStanza Message where
    from inj m@(Message{messageFrom=f}) = (\f' -> m{messageFrom = f'}) <$> inj f
    to inj m@(Message{messageTo=t}) = (\t' -> m{messageTo = t'}) <$> inj t
    lang inj m@(Message{messageLangTag=t}) =
        (\t' -> m{messageLangTag = t'}) <$> inj t
    sid inj m@(Message{messageID = i}) =
        ((\i' -> m{messageID = i'}) <$> inj i)
    payloadT inj m@(Message{messagePayload=pl}) =
        (\pl' -> m{messagePayload=pl'}) <$> traverseList inj pl


instance IsStanza MessageError where
    from inj m@(MessageError{messageErrorFrom=f}) =
        (\f' -> m{messageErrorFrom = f'}) <$> inj f
    to inj m@(MessageError{messageErrorTo=t}) =
        (\t' -> m{messageErrorTo = t'}) <$> inj t
    lang inj m@(MessageError{messageErrorLangTag=t}) =
        (\t' -> m{messageErrorLangTag = t'}) <$> inj t
    sid inj m@(MessageError{messageErrorID = i}) =
        ((\i' -> m{messageErrorID = i'}) <$> inj i)
    payloadT inj m@(MessageError{messageErrorPayload=pl}) =
        (\pl' -> m{messageErrorPayload=pl'}) <$> traverseList inj pl

instance IsStanza Presence where
    from inj m@(Presence{presenceFrom=f}) = (\f' -> m{presenceFrom = f'}) <$> inj f
    to inj m@(Presence{presenceTo=t}) = (\t' -> m{presenceTo = t'}) <$> inj t
    lang inj m@(Presence{presenceLangTag=t}) =
        (\t' -> m{presenceLangTag = t'}) <$> inj t
    sid inj m@(Presence{presenceID = i}) =
        ((\i' -> m{presenceID = i'}) <$> inj i)
    payloadT inj m@(Presence{presencePayload=pl}) =
        (\pl' -> m{presencePayload=pl'}) <$> traverseList inj pl

instance IsStanza PresenceError where
    from inj m@(PresenceError{presenceErrorFrom=f}) =
        (\f' -> m{presenceErrorFrom = f'}) <$> inj f
    to inj m@(PresenceError{presenceErrorTo=t}) =
        (\t' -> m{presenceErrorTo = t'}) <$> inj t
    lang inj m@(PresenceError{presenceErrorLangTag=t}) =
        (\t' -> m{presenceErrorLangTag = t'}) <$> inj t
    sid inj m@(PresenceError{presenceErrorID = i}) =
        ((\i' -> m{presenceErrorID = i'}) <$> inj i)
    payloadT inj m@(PresenceError{presenceErrorPayload=pl}) =
        (\pl' -> m{presenceErrorPayload=pl'}) <$> traverseList inj pl

instance IsStanza IQRequest where
    from inj m@(IQRequest{iqRequestFrom=f}) =
        (\f' -> m{iqRequestFrom = f'}) <$> inj f
    to inj m@(IQRequest{iqRequestTo=t}) =
        (\t' -> m{iqRequestTo = t'}) <$> inj t
    lang inj m@(IQRequest{iqRequestLangTag=t}) =
        (\t' -> m{iqRequestLangTag = t'}) <$> inj t
    sid inj m@(IQRequest{iqRequestID = i}) =
        ((\i' -> m{iqRequestID = i'}) <$> maybeNonempty inj i)
    payloadT inj m@(IQRequest{iqRequestPayload=pl}) =
        (\pl' -> m{iqRequestPayload=pl'}) <$> inj pl

instance IsStanza IQResult where
    from inj m@(IQResult{iqResultFrom=f}) =
        (\f' -> m{iqResultFrom = f'}) <$> inj f
    to inj m@(IQResult{iqResultTo=t}) =
        (\t' -> m{iqResultTo = t'}) <$> inj t
    lang inj m@(IQResult{iqResultLangTag=t}) =
        (\t' -> m{iqResultLangTag = t'}) <$> inj t
    sid inj m@(IQResult{iqResultID = i}) =
        ((\i' -> m{iqResultID = i'}) <$> maybeNonempty inj i)
    payloadT inj m@(IQResult{iqResultPayload=pl}) =
        (\pl' -> m{iqResultPayload=pl'}) <$> maybe (pure Nothing)
                                                   (fmap Just . inj) pl

instance IsStanza IQError where
    from inj m@(IQError{iqErrorFrom=f}) =
        (\f' -> m{iqErrorFrom = f'}) <$> inj f
    to inj m@(IQError{iqErrorTo=t}) =
        (\t' -> m{iqErrorTo = t'}) <$> inj t
    lang inj m@(IQError{iqErrorLangTag=t}) =
        (\t' -> m{iqErrorLangTag = t'}) <$> inj t
    sid inj m@(IQError{iqErrorID = i}) =
        ((\i' -> m{iqErrorID = i'}) <$> maybeNonempty inj i)
    payloadT inj m@(IQError{iqErrorPayload=pl}) =
        (\pl' -> m{iqErrorPayload=pl'}) <$> maybe (pure Nothing)
                                                  (fmap Just . inj) pl

liftLens :: (forall s. IsStanza s => Lens s a) -> Lens Stanza a
liftLens f inj (IQRequestS     s) = IQRequestS     <$> f inj s
liftLens f inj (IQResultS      s) = IQResultS      <$> f inj s
liftLens f inj (IQErrorS       s) = IQErrorS       <$> f inj s
liftLens f inj (MessageS       s) = MessageS       <$> f inj s
liftLens f inj (MessageErrorS  s) = MessageErrorS  <$> f inj s
liftLens f inj (PresenceS      s) = PresenceS      <$> f inj s
liftLens f inj (PresenceErrorS s) = PresenceErrorS <$> f inj s

liftTraversal :: (forall s. IsStanza s => Traversal s a) -> Traversal Stanza a
liftTraversal f inj (IQRequestS     s) = IQRequestS     <$> f inj s
liftTraversal f inj (IQResultS      s) = IQResultS      <$> f inj s
liftTraversal f inj (IQErrorS       s) = IQErrorS       <$> f inj s
liftTraversal f inj (MessageS       s) = MessageS       <$> f inj s
liftTraversal f inj (MessageErrorS  s) = MessageErrorS  <$> f inj s
liftTraversal f inj (PresenceS      s) = PresenceS      <$> f inj s
liftTraversal f inj (PresenceErrorS s) = PresenceErrorS <$> f inj s

instance IsStanza Stanza where
    from     = liftLens from
    to       = liftLens to
    lang     = liftLens lang
    sid      = liftLens sid
    payloadT = liftTraversal payloadT

maybeNonempty :: Lens Text (Maybe Text)
maybeNonempty inj x = (maybe Text.empty id)
                      <$> inj (if Text.null x then Nothing else Just x)


_IQRequest :: Prism Stanza IQRequest
_IQRequest = prism' IQRequestS fromIQRequestS
  where
    fromIQRequestS (IQRequestS s) = Just s
    fromIQRequestS _ = Nothing

_IQResult :: Prism Stanza IQResult
_IQResult = prism' IQResultS fromIQResultS
  where
    fromIQResultS (IQResultS s) = Just s
    fromIQResultS _ = Nothing

_IQError :: Prism Stanza IQError
_IQError = prism' IQErrorS fromIQErrorS
  where
    fromIQErrorS (IQErrorS s) = Just s
    fromIQErrorS _ = Nothing

_Message :: Prism Stanza Message
_Message = prism' MessageS fromMessageS
  where
    fromMessageS (MessageS s) = Just s
    fromMessageS _ = Nothing

_MessageError :: Prism Stanza MessageError
_MessageError = prism' MessageErrorS fromMessageErrorS
  where
    fromMessageErrorS (MessageErrorS s) = Just s
    fromMessageErrorS _ = Nothing

_Presence :: Prism Stanza Presence
_Presence = prism' PresenceS fromPresenceS
  where
    fromPresenceS (PresenceS s) = Just s
    fromPresenceS _ = Nothing

_PresenceError :: Prism Stanza PresenceError
_PresenceError = prism' PresenceErrorS fromPresenceErrorS
  where
    fromPresenceErrorS (PresenceErrorS s) = Just s
    fromPresenceErrorS _ = Nothing

class IsErrorStanza s where
    -- | Error element of the stanza
    stanzaError :: Lens s StanzaError

instance IsErrorStanza IQError where
    stanzaError inj m@IQError{iqErrorStanzaError = i} =
        (\i' -> m{iqErrorStanzaError = i'}) <$> inj i

instance IsErrorStanza MessageError where
    stanzaError inj m@MessageError{messageErrorStanzaError = i} =
        (\i' -> m{messageErrorStanzaError = i'}) <$> inj i

instance IsErrorStanza PresenceError where
    stanzaError inj m@PresenceError{presenceErrorStanzaError = i} =
        (\i' -> m{presenceErrorStanzaError = i'}) <$> inj i

class HasStanzaPayload s p | s -> p where
    -- | Payload element(s) of the stanza. Since the amount of elements possible
    -- in a stanza vary by type, this lens can't be used with a general
    -- 'Stanza'. There is, however, a more general Traversable that works with
    -- all stanzas (including 'Stanza'): 'payloadT'
    payload :: Lens s p

instance HasStanzaPayload IQRequest Element where
    payload inj m@IQRequest{iqRequestPayload = i} =
        (\i' -> m{iqRequestPayload = i'}) <$> inj i

instance HasStanzaPayload IQResult (Maybe Element) where
    payload inj m@IQResult{iqResultPayload = i} =
        (\i' -> m{iqResultPayload = i'}) <$> inj i

instance HasStanzaPayload IQError (Maybe Element) where
    payload inj m@IQError{iqErrorPayload = i} =
        (\i' -> m{iqErrorPayload = i'}) <$> inj i

instance HasStanzaPayload Message [Element] where
    payload inj m@Message{messagePayload = i} =
        (\i' -> m{messagePayload = i'}) <$> inj i

instance HasStanzaPayload MessageError [Element] where
    payload inj m@MessageError{messageErrorPayload = i} =
        (\i' -> m{messageErrorPayload = i'}) <$> inj i

instance HasStanzaPayload Presence [Element] where
    payload inj m@Presence{presencePayload = i} =
        (\i' -> m{presencePayload = i'}) <$> inj i

instance HasStanzaPayload PresenceError [Element] where
    payload inj m@PresenceError{presenceErrorPayload = i} =
        (\i' -> m{presenceErrorPayload = i'}) <$> inj i

iqRequestTypeL :: Lens IQRequest IQRequestType
iqRequestTypeL inj p@IQRequest{iqRequestType = tp} =
    (\tp' -> p{iqRequestType = tp'}) <$> inj tp


messageTypeL :: Lens Message MessageType
messageTypeL inj p@Message{messageType = tp} =
    (\tp' -> p{messageType = tp'}) <$> inj tp

presenceTypeL :: Lens Presence PresenceType
presenceTypeL inj p@Presence{presenceType = tp} =
    (\tp' -> p{presenceType = tp'}) <$> inj tp


-- StanzaError
-----------------------

stanzaErrorTypeL :: Lens StanzaError StanzaErrorType
stanzaErrorTypeL inj se@StanzaError{stanzaErrorType = x} =
    (\x' -> se{stanzaErrorType = x'}) <$> inj x

stanzaErrorConditionL :: Lens StanzaError StanzaErrorCondition
stanzaErrorConditionL inj se@StanzaError{stanzaErrorCondition = x} =
    (\x' -> se{stanzaErrorCondition = x'}) <$> inj x

stanzaErrorTextL :: Lens StanzaError (Maybe (Maybe LangTag, NonemptyText))
stanzaErrorTextL inj se@StanzaError{stanzaErrorText = x} =
    (\x' -> se{stanzaErrorText = x'}) <$> inj x

stanzaErrorApplL  :: Lens StanzaError (Maybe Element)
stanzaErrorApplL inj se@StanzaError{stanzaErrorApplicationSpecificCondition = x} =
    (\x' -> se{stanzaErrorApplicationSpecificCondition = x'}) <$> inj x


-- StreamConfiguration
-----------------------

preferredLangL :: Lens StreamConfiguration (Maybe LangTag)
preferredLangL inj sc@StreamConfiguration{preferredLang = x}
    = (\x' -> sc{preferredLang = x'}) <$> inj x

toJidL :: Lens StreamConfiguration (Maybe (Jid, Bool))
toJidL inj sc@StreamConfiguration{toJid = x}
    = (\x' -> sc{toJid = x'}) <$> inj x

connectionDetailsL :: Lens StreamConfiguration ConnectionDetails
connectionDetailsL inj sc@StreamConfiguration{connectionDetails = x}
    = (\x' -> sc{connectionDetails = x'}) <$> inj x

resolvConfL :: Lens StreamConfiguration ResolvConf
resolvConfL inj sc@StreamConfiguration{resolvConf = x}
    = (\x' -> sc{resolvConf = x'}) <$> inj x

tlsBehaviourL :: Lens StreamConfiguration TlsBehaviour
tlsBehaviourL inj sc@StreamConfiguration{tlsBehaviour = x}
    = (\x' -> sc{tlsBehaviour = x'}) <$> inj x


tlsParamsL :: Lens StreamConfiguration ClientParams
tlsParamsL inj sc@StreamConfiguration{tlsParams = x}
    = (\x' -> sc{tlsParams = x'}) <$> inj x

-- TLS parameters
-----------------

clientServerIdentificationL  :: Lens ClientParams (String, BS.ByteString)
clientServerIdentificationL inj cp@ClientParams{clientServerIdentification = x}
    = (\x' -> cp{clientServerIdentification = x'}) <$> inj x

clientSupportedL  :: Lens ClientParams Supported
clientSupportedL inj cp@ClientParams{clientSupported = x}
    = (\x' -> cp{clientSupported = x'}) <$> inj x

clientUseServerNameIndicationL  :: Lens ClientParams Bool
clientUseServerNameIndicationL inj
    cp@ClientParams{clientUseServerNameIndication = x}
    = (\x' -> cp{clientUseServerNameIndication = x'}) <$> inj x

supportedCiphersL :: Lens Supported [Cipher]
supportedCiphersL inj s@Supported{supportedCiphers = x}
    = (\x' -> s{supportedCiphers = x'}) <$> inj x

supportedVersionsL :: Lens Supported [TLS.Version]
supportedVersionsL inj s@Supported{supportedVersions = x}
    = (\x' -> s{supportedVersions = x'}) <$> inj x

-- SessionConfiguration
-----------------------
streamConfigurationL :: Lens SessionConfiguration StreamConfiguration
streamConfigurationL inj sc@SessionConfiguration{sessionStreamConfiguration = x}
    = (\x' -> sc{sessionStreamConfiguration = x'}) <$> inj x

onConnectionClosedL :: Lens SessionConfiguration (Session -> XmppFailure -> IO ())
onConnectionClosedL inj sc@SessionConfiguration{onConnectionClosed = x}
    = (\x' -> sc{onConnectionClosed = x'}) <$> inj x

sessionStanzaIDsL :: Lens SessionConfiguration (IO (IO Text))
sessionStanzaIDsL inj sc@SessionConfiguration{sessionStanzaIDs = x}
    = (\x' -> sc{sessionStanzaIDs = x'}) <$> inj x

ensableRosterL :: Lens SessionConfiguration Bool
ensableRosterL inj sc@SessionConfiguration{enableRoster = x}
    = (\x' -> sc{enableRoster = x'}) <$> inj x

onRosterPushL :: Lens SessionConfiguration (Maybe RosterPushCallback)
onRosterPushL = mkLens onRosterPush (\orp x -> x{onRosterPush = orp})

pluginsL :: Lens SessionConfiguration [Plugin]
pluginsL inj sc@SessionConfiguration{plugins = x}
    = (\x' -> sc{plugins = x'}) <$> inj x

onPresenceChangeL :: Lens SessionConfiguration (Maybe ( Jid -> PeerStatus
                                                        -> PeerStatus -> IO ()))
onPresenceChangeL inj sc@SessionConfiguration{onPresenceChange = x}
    = (\x' -> sc{onPresenceChange = x'}) <$> inj x

-- | Access clientServerIdentification inside tlsParams inside streamConfiguration
tlsServerIdentificationL  :: Lens SessionConfiguration (String, BS.ByteString)
tlsServerIdentificationL = streamConfigurationL
                         . tlsParamsL
                         . clientServerIdentificationL

-- | Access clientUseServerNameIndication inside tlsParams
tlsUseNameIndicationL :: Lens SessionConfiguration Bool
tlsUseNameIndicationL = streamConfigurationL
                      . tlsParamsL
                      . clientUseServerNameIndicationL

-- | Access supportedCiphers inside clientSupported inside tlsParams
tlsSupportedCiphersL :: Lens SessionConfiguration [Cipher]
tlsSupportedCiphersL =  streamConfigurationL
                     .  tlsParamsL . clientSupportedL . supportedCiphersL

-- | Access supportedVersions inside clientSupported inside tlsParams
tlsSupportedVersionsL :: Lens SessionConfiguration [TLS.Version]
tlsSupportedVersionsL = streamConfigurationL
                      . tlsParamsL . clientSupportedL . supportedVersionsL


-- Roster
------------------

verL :: Lens Roster (Maybe Text)
verL inj r@Roster{ver = x} = (\x' -> r{ver = x'}) <$> inj x

itemsL :: Lens Roster (Map.Map Jid Item)
itemsL inj r@Roster{items = x} = (\x' -> r{items = x'}) <$> inj x

-- Item
------------------

riApprovedL :: Lens Item Bool
riApprovedL inj i@Item{riApproved = x} = (\x' -> i{riApproved = x'}) <$> inj x

riAskL :: Lens Item Bool
riAskL inj i@Item{riAsk = x} = (\x' -> i{riAsk = x'}) <$> inj x

riJidL :: Lens Item Jid
riJidL inj i@Item{riJid = x} = (\x' -> i{riJid = x'}) <$> inj x

riNameL :: Lens Item (Maybe Text)
riNameL inj i@Item{riName = x} = (\x' -> i{riName = x'}) <$> inj x

riSubscriptionL :: Lens Item Subscription
riSubscriptionL inj i@Item{riSubscription = x} =
    (\x' -> i{riSubscription = x'}) <$> inj x

riGroupsL :: Lens Item [Text]
riGroupsL inj i@Item{riGroups = x} = (\x' -> i{riGroups = x'}) <$> inj x

-- Roster Update
-------------------

_RosterUpdateRemove :: Prism RosterUpdate Jid
_RosterUpdateRemove = prism' RosterUpdateRemove fromRosterUpdateRemove
  where
    fromRosterUpdateRemove (RosterUpdateRemove jid) = Just jid
    fromRosterUpdateRemove RosterUpdateAdd{} = Nothing

_RosterUpdateAdd :: Prism RosterUpdate Item
_RosterUpdateAdd = prism' RosterUpdateAdd fromRosterUpdateAdd
  where
    fromRosterUpdateAdd RosterUpdateRemove{} = Nothing
    fromRosterUpdateAdd (RosterUpdateAdd item) = Just item


-- QueryItem
-------------------
qiApprovedL :: Lens QueryItem (Maybe Bool)
qiApprovedL inj i@QueryItem{qiApproved = x} =
    (\x' -> i{qiApproved = x'}) <$> inj x

qiAskL :: Lens QueryItem Bool
qiAskL inj i@QueryItem{qiAsk = x} = (\x' -> i{qiAsk = x'}) <$> inj x

qiJidL :: Lens QueryItem Jid
qiJidL inj i@QueryItem{qiJid = x} = (\x' -> i{qiJid = x'}) <$> inj x

qiNameL :: Lens QueryItem (Maybe Text)
qiNameL inj i@QueryItem{qiName = x} = (\x' -> i{qiName = x'}) <$> inj x

qiSubscriptionL :: Lens QueryItem (Maybe Subscription)
qiSubscriptionL inj i@QueryItem{qiSubscription = x} =
    (\x' -> i{qiSubscription = x'}) <$> inj x

qiGroupsL :: Lens QueryItem [Text]
qiGroupsL inj i@QueryItem{qiGroups = x} = (\x' -> i{qiGroups = x'}) <$> inj x

queryVerL :: Lens Query (Maybe Text)
queryVerL inj i@Query{queryVer = x} = (\x' -> i{queryVer = x'}) <$> inj x

queryItemsL :: Lens Query [QueryItem]
queryItemsL inj i@Query{queryItems = x} = (\x' -> i{queryItems = x'}) <$> inj x


-- IM
-------------------


bodyLangL :: Lens MessageBody (Maybe LangTag)
bodyLangL inj m@MessageBody{bodyLang = bl} = (\bl' -> m{bodyLang = bl'}) <$> inj bl

bodyContentL :: Lens MessageBody Text
bodyContentL inj m@MessageBody{bodyContent = bc} =
    (\bc' -> m{bodyContent = bc'}) <$> inj bc

threadIdL :: Lens MessageThread Text
threadIdL inj m@MessageThread{threadID = bc} =
    (\bc' -> m{threadID = bc'}) <$> inj bc

threadParentL :: Lens MessageThread (Maybe Text)
threadParentL inj m@MessageThread{threadParent = bc} =
    (\bc' -> m{threadParent = bc'}) <$> inj bc

subjectLangL :: Lens MessageSubject (Maybe LangTag)
subjectLangL inj m@MessageSubject{subjectLang = bc} =
    (\bc' -> m{subjectLang = bc'}) <$> inj bc

subjectContentL :: Lens MessageSubject Text
subjectContentL inj m@MessageSubject{subjectContent = bc} =
    (\bc' -> m{subjectContent = bc'}) <$> inj bc

imThreadL :: Lens InstantMessage (Maybe MessageThread)
imThreadL inj m@InstantMessage{imThread = bc} =
    (\bc' -> m{imThread = bc'}) <$> inj bc

imSubjectL :: Lens InstantMessage [MessageSubject]
imSubjectL inj m@InstantMessage{imSubject = bc} =
    (\bc' -> m{imSubject = bc'}) <$> inj bc

imBodyL :: Lens InstantMessage [MessageBody]
imBodyL inj m@InstantMessage{imBody = bc} =
    (\bc' -> m{imBody = bc'}) <$> inj bc

-- IM Presence
------------------

showStatusL :: Lens IMPresence (Maybe ShowStatus)
showStatusL inj m@IMP{showStatus = bc} =
    (\bc' -> m{showStatus = bc'}) <$> inj bc

statusL :: Lens IMPresence (Maybe Text)
statusL inj m@IMP{status = bc} =
    (\bc' -> m{status = bc'}) <$> inj bc

priorityL :: Lens IMPresence (Maybe Int)
priorityL inj m@IMP{priority = bc} =
    (\bc' -> m{priority = bc'}) <$> inj bc

-- StreamFeatures
-------------------

featureTlsL :: Lens StreamFeatures (Maybe Bool)
featureTlsL = mkLens streamFeaturesTls (\x sf -> sf{streamFeaturesTls = x})

featureMechanismsL :: Lens StreamFeatures [Text]
featureMechanismsL =
    mkLens streamFeaturesMechanisms (\x sf -> sf{streamFeaturesMechanisms = x})

featureRosterVerL :: Lens StreamFeatures (Maybe Bool)
featureRosterVerL =
    mkLens streamFeaturesRosterVer (\x sf -> sf{streamFeaturesRosterVer = x})

featurePreApprovalL :: Lens StreamFeatures Bool
featurePreApprovalL =
    mkLens streamFeaturesPreApproval (\x sf -> sf{streamFeaturesPreApproval = x})

featuresOtherL :: Lens StreamFeatures [Element]
featuresOtherL =
    mkLens streamFeaturesOther (\x sf -> sf{streamFeaturesOther = x})