doNick ::
  UserInfo   {- ^ old user infomation -} ->
  Identifier {- ^ new nickname        -} ->
  IrcConnection -> Logic IrcConnection
doNick who newnick conn =
  do stamp <- getStamp
     let m = defaultIrcMessage
                { _mesgType = NickMsgType newnick
                , _mesgSender = who
                , _mesgStamp = stamp
                }

     let conn1 | isMyNick (userNick who) conn =
                        set connNick newnick conn
               | otherwise = conn

         conn2 = set (connUsers . at newnick)
                     (view (connUsers . at (userNick who)) conn1)
               $ set (connUsers . at (userNick who))
                     Nothing
               $ conn1

     iforOf (connChannels . itraversed) conn2 (updateChannel m)

  where
  oldnick = userNick who

  updateChannel :: IrcMessage -> Identifier -> IrcChannel -> Logic IrcChannel
  updateChannel m tgt chan
    | has (chanUsers . ix oldnick) chan
     = do recordFor tgt m
          pure $ set (chanUsers . at oldnick) Nothing
               $ set (chanUsers . at newnick) (view (chanUsers . at oldnick) chan)
               $ chan
    | otherwise = pure chan


-- | Update the 'IrcConnection' when a user parts from a channel.
doNick ::
  UserInfo   {- ^ old user infomation -} ->
  Identifier {- ^ new nickname        -} ->
  IrcConnection -> Logic IrcConnection
doNick who newnick conn =
  do stamp <- getStamp
     let m = defaultIrcMessage
                { _mesgType = NickMsgType newnick
                , _mesgSender = who
                , _mesgStamp = stamp
                }

     let conn1 | isMyNick (userNick who) conn =
                        set connNick newnick conn
               | otherwise = conn

         conn2 = set (connUsers . at newnick)
                     (view (connUsers . at (userNick who)) conn1)
               $ set (connUsers . at (userNick who))
                     Nothing
               $ conn1

     iforOf (connChannels . itraversed) conn2 (updateChannel m)

  where
  oldnick = userNick who

  updateChannel :: IrcMessage -> Identifier -> IrcChannel -> Logic IrcChannel
  updateChannel m tgt chan
    | has (chanUsers . ix oldnick) chan
     = do recordFor tgt m
          pure $ set (chanUsers . at oldnick) Nothing
               $ set (chanUsers . at newnick) (view (chanUsers . at oldnick) chan)
               $ chan
    | otherwise = pure chan


-- | Update the 'IrcConnection' when a user parts from a channel.
doNick who newnick conn =
  do stamp <- getStamp
     let m = defaultIrcMessage
                { _mesgType = NickMsgType newnick
                , _mesgSender = who
                , _mesgStamp = stamp
                }

     let conn1 | isMyNick (userNick who) conn =
                        set connNick newnick conn
               | otherwise = conn

         conn2 = set (connUsers . at newnick)
                     (view (connUsers . at (userNick who)) conn1)
               $ set (connUsers . at (userNick who))
                     Nothing
               $ conn1

     iforOf (connChannels . itraversed) conn2 (updateChannel m)

  where
  oldnick = userNick who

  updateChannel :: IrcMessage -> Identifier -> IrcChannel -> Logic IrcChannel
  updateChannel m tgt chan
    | has (chanUsers . ix oldnick) chan
     = do recordFor tgt m
          pure $ set (chanUsers . at oldnick) Nothing
               $ set (chanUsers . at newnick) (view (chanUsers . at oldnick) chan)
               $ chan
    | otherwise = pure chan


-- | Update the 'IrcConnection' when a user parts from a channel.