{- This file is part of irc-fun-types.
 -
 - Written in 2015, 2016 by fr33domlover <fr33domlover@riseup.net>.
 -
 - ♡ Copying is an act of love. Please copy, reuse and share.
 -
 - The author(s) have dedicated all copyright and related and neighboring
 - rights to this software to the public domain worldwide. This software is
 - distributed without any warranty.
 -
 - You should have received a copy of the CC0 Public Domain Dedication along
 - with this software. If not, see
 - <http://creativecommons.org/publicdomain/zero/1.0/>.
 -}

module Network.Irc.Types
    ( -- * Type Aliases, Newtypes, Other Basics
      module Network.Irc.Types.Base
      -- * Message Parts
    , Prefix (..)
    , UserAddress (..)
    , Target (..)
    , Address (..)
    , Host (..)
    , Command (..)
      -- * Mask
    , MaskPart (..)
    , Mask (..)
    , TargetMask (..)
      -- * Message
    , GenericMessage (..)
    , SpecificMessage (..)
    , MessageTarget (..)
    , Query (..)
    , Message (..)
    , AnalysisError (..)
    , ArgSpec (..)
      -- * Modes
    , UserMode (..)
    , ChannelMode (..)
    , ChannelModeType (..)
      -- * Reply
    , GenericReply (..)
    , Reply (..)
    , SpecificReply (..)
    )
where

import Data.Text (Text)
import Network.Irc.Types.Base

-------------------------------------------------------------------------------
-- Message Parts
-------------------------------------------------------------------------------

-- | IRC message prefix.
data Prefix
    -- | The server which sends the message
    = PrefixServer Hostname
    -- | The user which sends the message (if the message comes from a user)
    | PrefixNick Nickname (Maybe Username) (Maybe Host)
    deriving (Eq, Show)

-- | User address containing a name part (as in "user") and an optional host
-- part (as in "user@host").
data UserAddress = UserAddress Username (Maybe Host) deriving (Eq, Show)

-- | Some IRC commands operate on a target, that is either a single user or an
-- entire IRC server.
data Target = NickTarget Nickname | ServerTarget Hostname deriving (Eq, Show)

-- | An IP address.
data Address = IPv4 Text | IPv6 Text deriving (Eq, Show)

-- | A host, either a hostname (e.g. @irc.freenode.net@) or an IP address, or a
-- cloak assigned by the server.
data Host = HostByName Hostname | HostByAddr Address | HostCloak Text
    deriving (Eq, Show)

-- | An IRC command code. Either a named command, or a numeric one.
data Command = NamedCmd CmdName | NumericCmd CmdNumber
    deriving (Eq, Show)

-------------------------------------------------------------------------------
-- Masks
-------------------------------------------------------------------------------

-- | Part of a 'Mask'.
data MaskPart
    -- | A literal character.
    = MaskChar Char
    -- | A wildcard which matches a single character.
    | MaskWildOne
    -- | A wildcard which matches zero or more characters.
    | MaskWildMany
    deriving (Eq, Show)

-- | A pattern for matching host and server names. A mask in the protocol is a
-- string which consists of literal characters and wildcards. There are two
-- wildcards: @'?'@, which matches a single character; @'*'@, which matches any
-- number of characters (zero or more).
newtype Mask = Mask [MaskPart] deriving (Eq, Show)

-- | Some IRC command parameters are targets, and one way to specify a target
-- is using a 'Mask'.
data TargetMask
    -- | Host mask, prefixed with @'#'@ in the protocol. Selects all the users
    -- whose host matches the mask.
    = HostMask Mask
    -- | Server mask, prefixed with @'$'@ in the protocol. Selects all the
    -- users connected to a server which matches the mask.
    | ServerMask Mask
    deriving (Eq, Show)

-------------------------------------------------------------------------------
-- Message
-------------------------------------------------------------------------------

-- | A generic IRC message. It consists of a sender prefix, command name
-- (string or numeric) and optionally a list of parameters.
data GenericMessage = GenericMessage
    { gmPrefix  :: Maybe Prefix
    , gmCommand :: Command
    , gmParams  :: [Text]
    }
    deriving Show

-- | An IRC message of a specific type. This includes only messages whose
-- command is a name. Messages with a numeric command are handled in the
-- "Network.IRC.Fun.Messages.Reply" module.
data SpecificMessage = SpecificMessage (Maybe Prefix) Message deriving Show

-- | Some IRC commands have targets. For example, to which channel to send a
-- given text message. Targets are specified as parameters in IRC messages.
data MessageTarget
    -- | An IRC channel.
    = ChannelTarget Channel
    -- | An IRC user. Possible combinations in the protocol are:
    --
    -- * Address              (address host is required)
    -- * Address and server   (address host is optional)
    -- * Nickname
    -- * Nickname and address (address host is required)
    | UserTarget (Maybe Nickname) (Maybe UserAddress) (Maybe Hostname)
    -- | A mask which matches the server or host of the users to whom the
    -- message should be sent.
    | MaskTarget TargetMask
    deriving Show

-- | A server query. Can be sent through a 'StatsMessage'.
data Query
    -- | TODO
    = Connections
    -- | TODO
    | CommandUsage
    -- | TODO
    | PrivilegedUsers
    -- | TODO
    | Uptime
    -- | TODO
    | OtherQuery Char
    deriving Show

-- TODO in all the either-servername-mask occurences, make sure in the RFC
-- that they are "<target> may contain wildcards" cases, and replace them with
-- plain Hostname - let's treat mask as a user mask (for bans etc.)
-- TODO in all the <target> cases, do they mean a user can be specified too,
-- to mean "the server this user is connected to"? try with some IRC servers
-- over telnet, and change Hostname to Target if yes. Note that servername
-- has '.' chars while a nickname can't, so that's how one tells which is which
-- Also compare with existing irc packages, how they model these messages

-- | An IRC message of specific type (excluding the optional sender prefix, see
-- 'SpecificMessage').
data Message
-- Connection registration
    = PassMessage Password
    | NickMessage Nickname
    -- | first bool = whether invisible, second bool = whether sees wallops
    | UserMessage Username Bool Bool RealName
    | OperMessage Username Password
    -- | first list: modes to remove (-), second list: modes to add (+)
    | UserModeMessage Nickname [UserMode] [UserMode]
    | ServiceMessage Nickname Mask Text
    | QuitMessage (Maybe Comment)
    | SQuitMessage Hostname Comment
-- Channel operations
    | JoinMessage (Maybe ([Channel], [ChannelKey]))
    | PartMessage [Channel] (Maybe Comment)
    -- first: remove (-), second: add/set (+)
    | ChannelModeMessage [ChannelMode] [ChannelMode]
    | TopicMessage Channel (Maybe ChannelTopic)
    | NamesMessage [Channel] (Maybe Hostname)
    | ListMessage [Channel] (Maybe Hostname)
    | InviteMessage Nickname Channel
    | KickMessage [Channel] [Username] (Maybe Comment)
-- Sending messages
    | PrivMsgMessage MessageTarget MsgContent
    | PrivActionMessage MessageTarget MsgContent
    | NoticeMessage MessageTarget MsgContent
-- Server queries and commands
    | MotdMessage (Maybe Hostname)
    | LusersMessage (Maybe (Mask, Maybe Hostname))
    | VersionMessage (Maybe (Either Hostname Mask))
    | StatsMessage (Maybe (Query, Maybe (Either Hostname Mask)))
    | LinksMessage (Maybe (Maybe (Either Hostname Mask), Mask))
    | TimeMessage (Maybe (Either Hostname Mask))
    | ConnectMessage Hostname PortNumber (Either Hostname Mask)
    | TraceMessage (Maybe Target)
    | AdminMessage (Maybe Target)
    | InfoMessage (Maybe Target)
-- Service query and commands
    | ServlistMessage (Maybe (Mask, Maybe Text))
    | SQueryMessage ServiceName MsgContent
-- User based queries
    | WhoMessage (Maybe (Mask, Bool))
    | WhoisMessage (Maybe Hostname) [Mask]
    | WhowasMessage [Nickname] (Maybe (Int, Maybe Hostname))
-- Miscellaneous messages
    | KillMessage Nickname Comment
    | PingMessage Hostname (Maybe Hostname)
    | PongMessage Hostname (Maybe Hostname)
    | ErrorMessage Text
-- Optional features
    | AwayMessage (Maybe MsgContent)
    | RehashMessage
    | DieMessage
    | RestartMessage
    | SummonMessage Username (Maybe (Hostname, Maybe Channel))
    | UsersMessage (Maybe Hostname)
    | WallopsMessage MsgContent
    | UserhostMessage [Nickname]
    | IsonMessage [Nickname]
    deriving Show

-------------------------------------------------------------------------------
-- Modes
-------------------------------------------------------------------------------

-- | TODO
data UserMode
    -- http://toxin.jottit.com/freenode_user_modes
    = UModeInvisible
    | UModeCallerID
    | UModeSeeWallops
    | UModeDeaf
    | UModeNoForwarding
    | UModeBlockUnidentified
    | UModeConnectedViaSSL
    -- from the irc rfc, MODE message (section 3.1.5)
    | UModeAway
    | UModeRestricted
    | UModeOperator
    | UModeLocalOperator
    | UModeSeeNotices
    | UModeOther Char
    deriving (Eq, Show)

-- | TODO
data ChannelMode
    = CModeCreator
    | CModeOperator
    | CModeVoice
    | CModeAnonymous
    | CModeInviteOnly
    | CModeModerated
    | CModeNoMessagesFromOutside
    | CModeQuiet
    | CModePrivate
    | CModeSecret
    | CModeServerReop
    | CModeTopicSettableByChannelOpOnly
    | CModeKey (Maybe ChannelKey)
    | CModeUserLimit (Maybe Int)
    | CModeBanMask (Maybe Mask)
    | CModeExceptionMask (Maybe Mask)
    | CModeInvitationMask (Maybe Mask)
    | CModeOther Char [Text]
    | CModeFreenodeQuiet (Maybe Mask)
    deriving (Eq, Show)

data ChannelModeType
    = ModeTypeList
    | ModeTypeSetting
    | ModeTypeMaybeSetting
    | ModeTypeFlag

-------------------------------------------------------------------------------
-- Reply
-------------------------------------------------------------------------------

-- | TODO
data CommandResponseCode
    -- | TODO
    = ReplyWelcome
    -- | TODO
    | ReplyYourHost
    -- | TODO
    | ReplyCreated
    -- | TODO
    | ReplyMyInfo
    -- | TODO
    | ReplyBounce
    -- | TODO
    | ReplyUserHost
    -- | TODO
    | ReplyIsOn
    -- | TODO
    | ReplyAway
    -- | TODO
    | ReplyUnAway
    -- | TODO
    | ReplyNoAway
    -- | TODO
    | ReplyWhoIsUser
    -- | TODO
    | ReplyWhoIsServer
    -- | TODO
    | ReplyWhoIsOperator
    -- | TODO
    | ReplyWhoIsIdle
    -- | TODO
    | ReplyEndOfWhoIs
    -- | TODO
    | ReplyWhoIsChannels
    -- | TODO
    | ReplyWhoWasUser
    -- | TODO
    | ReplyEndOfWhoWas
    -- | TODO
    | ReplyListStart
    -- | TODO
    | ReplyList
    -- | TODO
    | ReplyListEnd
    -- | TODO
    | ReplyUniqueOpIs
    -- | TODO
    | ReplyChannelModeIs
    -- | TODO
    | ReplyNoTopic
    -- | TODO
    | ReplyTopic
    -- | TODO
    | ReplyInviting
    -- | TODO
    | ReplySummoning
    -- | TODO
    | ReplyInviteList
    -- | TODO
    | ReplyEndOfInviteList
    -- | TODO
    | ReplyExceptList
    -- | TODO
    | ReplyEndOfExceptList
    -- | TODO
    | ReplyVersion
    -- | TODO
    | ReplyWhoReply
    -- | TODO
    | ReplyEndOfWho
    -- | TODO
    | ReplyNameReply
    -- | TODO
    | ReplyEndOfNames
    -- | TODO
    | ReplyLinks
    -- | TODO
    | ReplyEndOfLinks
    -- | TODO
    | ReplyBanList
    -- | TODO
    | ReplyEndOfBanList
    -- | TODO
    | ReplyInfo
    -- | TODO
    | ReplyEndOfInfo
    -- | TODO
    | ReplyMotdStart
    -- | TODO
    | ReplyMessageOfTheDay
    -- | TODO
    | ReplyEndOfMessageOfTheDay
    -- | TODO
    | ReplyYoureOper
    -- | TODO
    | ReplyRehashing
    -- | TODO
    | ReplyYoureService
    -- | TODO
    | ReplyTime
    -- | TODO
    | ReplyUsersStart
    -- | TODO
    | ReplyUsers
    -- | TODO
    | ReplyEndOfUsers
    -- | TODO
    | ReplyNoUsers
    -- | TODO
    | ReplyTraceLink
    -- | TODO
    | ReplyTraceConnecting
    -- | TODO
    | ReplyTraceHandshake
    -- | TODO
    | ReplyTraceUnknown
    -- | TODO
    | ReplyTraceOperator
    -- | TODO
    | ReplyTraceUser
    -- | TODO
    | ReplyTraceServer
    -- | TODO
    | ReplyTraceService
    -- | TODO
    | ReplyTraceNewType
    -- | TODO
    | ReplyTraceClass
    -- | TODO
    | ReplyTraceReconnect
    -- | TODO
    | ReplyTraceLog
    -- | TODO
    | ReplyTraceEnd
    -- | TODO
    | ReplyStatsLinkInfo
    -- | TODO
    | ReplyStatsCommands
    -- | TODO
    | ReplyEndOfStats
    -- | TODO
    | ReplyStatsUptime
    -- | TODO
    | ReplyStatsOLine
    -- | TODO
    | ReplyUModeIs
    -- | TODO
    | ReplyServList
    -- | TODO
    | ReplyServListEnd
    -- | TODO
    | ReplyLuserClient
    -- | TODO
    | ReplyLuserOp
    -- | TODO
    | ReplyLuserUnknown
    -- | TODO
    | ReplyLuserChannels
    -- | TODO
    | ReplyLuserMe
    -- | TODO
    | ReplyAdminMe
    -- | TODO
    | ReplyAdminLocation1
    -- | TODO
    | ReplyAdminLocation2
    -- | TODO
    | ReplyAdminEmail
    -- | TODO
    | ReplyTryAgain
    -- | TODO
    | ReplyFreenodeISupport
    deriving (Eq, Show)

data ErrorReplyCode
    -- | TODO
    = ErrorNoSuchNick
    -- | TODO
    | ErrorNoSuchServer
    -- | TODO
    | ErrorNoSuchChannel
    -- | TODO
    | ErrorCannotSendToChannel
    -- | TODO
    | ErrorTooManyChannels
    -- | TODO
    | ErrorWasNoSuchNick
    -- | TODO
    | ErrorTooManyTargets
    -- | TODO
    | ErrorNoSuchService
    -- | TODO
    | ErrorNoOrigin
    -- | TODO
    | ErrorNoRecipient
    -- | TODO
    | ErrorNoTextToSend
    -- | TODO
    | ErrorNoTopLevel
    -- | TODO
    | ErrorWildTopLevel
    -- | TODO
    | ErrorBadMask
    -- | TODO
    | ErrorUnknownCommand
    -- | TODO
    | ErrorNoMessageOfTheDay
    -- | TODO
    | ErrorNoAdminInfo
    -- | TODO
    | ErrorFileError
    -- | TODO
    | ErrorNoNicknameGiven
    -- | TODO
    | ErrorErroneousNickname
    -- | TODO
    | ErrorNicknameInUse
    -- | TODO
    | ErrorNickCollision
    -- | TODO
    | ErrorUnavailableResource
    -- | TODO
    | ErrorUserNotInChannel
    -- | TODO
    | ErrorNotOnChannel
    -- | TODO
    | ErrorUserOnChannel
    -- | TODO
    | ErrorNoLogin
    -- | TODO
    | ErrorSummonDisabled
    -- | TODO
    | ErrorUsersDisabled
    -- | TODO
    | ErrorNotRegistered
    -- | TODO
    | ErrorNeedMoreParams
    -- | TODO
    | ErrorAlreadyRegistered
    -- | TODO
    | ErrorNoPermissionForHost
    -- | TODO
    | ErrorPasswordMismatch
    -- | TODO
    | ErrorYoureBannedCreep
    -- | TODO
    | ErrorYouWillBeBanned
    -- | TODO
    | ErrorKeySet
    -- | TODO
    | ErrorChannelIsFull
    -- | TODO
    | ErrorUnknownMode
    -- | TODO
    | ErrorInviteOnlyChannel
    -- | TODO
    | ErrorBannedFromChannel
    -- | TODO
    | ErrorBadChannelKey
    -- | TODO
    | ErrorBadChannelMask
    -- | TODO
    | ErrorNoChannelModes
    -- | TODO
    | ErrorBanListFull
    -- | TODO
    | ErrorNoPrivileges
    -- | TODO
    | ErrorChannelOpPrivilegesNeeded
    -- | TODO
    | ErrorCantKillServer
    -- | TODO
    | ErrorRestricted
    -- | TODO
    | ErrorUniqueOpPrivilegesNeeded
    -- | TODO
    | ErrorNoOperHost
    -- | TODO
    | ErrorUModeUnknownFlag
    -- | TODO
    | ErrorUsersDontMatch
    deriving (Eq, Show)

data ReservedReplyCode
    -- | TODO
    = ReservedReplyServiceInfo
    -- | TODO
    | ReservedReplyEndOfServices
    -- | TODO
    | ReservedReplyService
    -- | TODO
    | ReservedReplyNone
    -- | TODO
    | ReservedReplyWhoIsChannelOp
    -- | TODO
    | ReservedReplyKillDone
    -- | TODO
    | ReservedReplyClosing
    -- | TODO
    | ReservedReplyCloseEnd
    -- | TODO
    | ReservedReplyInfoStart
    -- | TODO
    | ReservedReplyMyPortIs
    -- | TODO
    | ReservedReplyStatsCLine
    -- | TODO
    | ReservedReplyStatsNLine
    -- | TODO
    | ReservedReplyStatsILine
    -- | TODO
    | ReservedReplyStatsKLine
    -- | TODO
    | ReservedReplyStatsQLine
    -- | TODO
    | ReservedReplyStatsYLine
    -- | TODO
    | ReservedReplyStatsVLine
    -- | TODO
    | ReservedReplyStatsLLine
    -- | TODO
    | ReservedReplyStatsHLine
    -- | TODO
    | ReservedReplyStatsSLine
    -- | TODO
    | ReservedReplyStatsPing
    -- | TODO
    | ReservedReplyStatsBLine
    -- | TODO
    | ReservedReplyStatsDLine
    deriving (Eq, Show)

-- | TODO
data ReservedErrorCode
    -- | TODO
    = ReservedErrorNoServiceHost
    deriving (Eq, Show)

-- | TODO
data GenericReply = GenericReply
    { grSender :: Hostname
    , grCode   :: CmdNumber
    , grTarget :: Target
    , grParams :: [Parameter]
    }
    deriving Show

-- | TODO
data Reply
    -- | TODO
    = WelcomeReply
    -- | TODO
    | YourHostReply
    -- | TODO
    | CreatedReply
    -- | TODO
    | MyInfoReply
    -- | TODO
    | BounceReply
    -- | TODO
    | ISupportReply
    -- | TODO
    | UserHostReply
    -- | TODO
    | IsOnReply
    -- | TODO
    | AwayReply
    -- | TODO
    | UnAwayReply
    -- | TODO
    | NoAwayReply
    -- | TODO
    | WhoIsUserReply
    -- | TODO
    | WhoIsServerReply
    -- | TODO
    | WhoIsOperatorReply
    -- | TODO
    | WhoIsIdleReply
    -- | TODO
    | EndOfWhoIsReply
    -- | TODO
    | WhoIsChannelsReply
    -- | TODO
    | WhoWasUserReply
    -- | TODO
    | EndOfWhoWasReply
    -- | TODO
    | ListStartReply
    -- | TODO
    | ListReply
    -- | TODO
    | ListEndReply
    -- | TODO
    | UniqueOpIsReply
    -- | TODO
    | ChannelModeIsReply
    -- | TODO
    | NoTopicReply
    -- | TODO
    | TopicReply
    -- | TODO
    | InvitingReply
    -- | TODO
    | SummoningReply
    -- | TODO
    | InviteListReply
    -- | TODO
    | EndOfInviteListReply
    -- | TODO
    | ExceptListReply
    -- | TODO
    | EndOfExceptListReply
    -- | TODO
    | VersionReply
    -- | TODO
    | WhoReply
    -- | TODO
    | EndOfWhoReply
    -- | Lists members of IRC channels. Sent when JOINing a channel to list the
    -- users in that channel, or as a response to the NAMES command.
    | NamesReply ChannelPrivacy Channel [(Privilege, Nickname)]
    -- | Sent after a sequence of 0 or more 'NameReply's, to signal that the
    -- list transmission is finished.
    | EndOfNamesReply
    -- | TODO
    | LinksReply
    -- | TODO
    | EndOfLinksReply
    -- | TODO
    | BanListReply
    -- | TODO
    | EndOfBanListReply
    -- | TODO
    | InfoReply
    -- | TODO
    | EndOfInfoReply
    -- | TODO
    | MotdStartReply
    -- | TODO
    | MessageOfTheDayReply
    -- | TODO
    | EndOfMessageOfTheDayReply
    -- | TODO
    | YoureOperReply
    -- | TODO
    | RehashingReply
    -- | TODO
    | YoureServiceReply
    -- | TODO
    | TimeReply
    -- | TODO
    | UsersStartReply
    -- | TODO
    | UsersReply
    -- | TODO
    | EndOfUsersReply
    -- | TODO
    | NoUsersReply
    -- | TODO
    | TraceLinkReply
    -- | TODO
    | TraceConnectingReply
    -- | TODO
    | TraceHandshakeReply
    -- | TODO
    | TraceUnknownReply
    -- | TODO
    | TraceOperatorReply
    -- | TODO
    | TraceUserReply
    -- | TODO
    | TraceServerReply
    -- | TODO
    | TraceServiceReply
    -- | TODO
    | TraceNewTypeReply
    -- | TODO
    | TraceClassReply
    -- | TODO
    | TraceReconnectReply
    -- | TODO
    | TraceLogReply
    -- | TODO
    | TraceEndReply
    -- | TODO
    | StatsLinkInfoReply
    -- | TODO
    | StatsCommandsReply
    -- | TODO
    | EndOfStatsReply
    -- | TODO
    | StatsUptimeReply
    -- | TODO
    | StatsOLineReply
    -- | TODO
    | UModeIsReply
    -- | TODO
    | ServListReply
    -- | TODO
    | ServListEndReply
    -- | TODO
    | LuserClientReply
    -- | TODO
    | LuserOpReply
    -- | TODO
    | LuserUnknownReply
    -- | TODO
    | LuserChannelsReply
    -- | TODO
    | LuserMeReply
    -- | TODO
    | AdminMeReply
    -- | TODO
    | AdminLocation1Reply
    -- | TODO
    | AdminLocation2Reply
    -- | TODO
    | AdminEmailReply
    -- | TODO
    | TryAgainReply
    deriving (Eq, Show)

-- | An IRC server numeric reply of a specific type.
data SpecificReply = SpecificReply Hostname Target Reply deriving Show

-- | Specifies the number of parameter a certain IRC message type accepts.
data ArgSpec
    = Exactly Int
    | AtLeast Int
    | AtMost Int
    | Between Int Int
    | OneOf [Int]
    deriving (Show)

-- | An error occuring while analyzing a parsed IRC protocol message into a
-- specific message type, such as 'JoinMessage'.
data AnalysisError
    = WrongNumArgs Int (Maybe ArgSpec)
    | InvalidArg (Maybe Int) (Maybe Text) (Maybe Text)
    | OtherError Text
    deriving (Show)