{-# LANGUAGE OverloadedStrings #-}

module System.Log.CEF.Extensions
  ( Extensions
  , emptyExtensions
  , extensionsBuilder
  , customExtension
  -- ** Predefined Extensions
  -- | See /Chapter 2: ArcSight Extension Dictionary/ from the reference
  , IPv6Address
  , IPv4Address
  , MACAddress
  , TimeStamp
  , deviceAction
  , deviceCustomIPv6Address1
  , deviceCustomIPv6Address2
  , deviceCustomIPv6Address3
  , deviceCustomIPv6Address4
  , applicationProtocol
  , deviceEventCategory
  , deviceCustomFloatingPoint1
  , deviceCustomFloatingPoint2
  , deviceCustomFloatingPoint3
  , deviceCustomFloatingPoint4
  , deviceCustomNumber1
  , deviceCustomNumber2
  , deviceCustomNumber3
  , deviceCustomNumber4
  , baseEventCount
  , deviceCustomString1
  , deviceCustomString2
  , deviceCustomString3
  , deviceCustomString4
  , deviceCustomString5
  , deviceCustomString6
  , destinationDnsDomain
  , destinationServiceName
  , destinationTranslatedAddress
  , destinationTranslatedPort
  , deviceCustomDate1
  , deviceCustomDate2
  , deviceDirectionInbound
  , deviceDirectionOutbound
  , deviceDnsDomain
  , deviceExternalId
  , deviceFacility
  , deviceInboundInterface
  , deviceMacAddress
  , deviceNtDomain
  , deviceOutboundInterface
  , deviceProcessName
  , deviceTranslatedAddress
  , destinationHostName
  , destinationMacAddress
  , destinationNtDomain
  , destinationProcessId
  , destinationUserPrivileges
  , destinationProcessName
  , destinationPort
  , destinationAddress
  , destinationUserId
  , destinationUserName
  , deviceAddress
  , deviceHostName
  , deviceProcessId
  , endTime
  , externalId
  , fileCreateTime
  , fileHash
  , fileId
  , fileModificationTime
  , filePath
  , filePermission
  , fileType
  , fileName
  , fileSize
  , bytesIn
  , message
  , oldFileCreateTime
  , oldFileHash
  , oldFileId
  , oldFileModificationTime
  , oldFileName
  , oldFilePath
  , oldFilePermission
  , oldFileSize
  , oldFileType
  , bytesOut
  , eventOutcome
  , transportProtocol
  , reason
  , requestURL
  , requestClientApplication
  , requestCookies
  , requestMethod
  , receiptTime
  , sourceHostName
  , sourceMacAddress
  , sourceNtDomain
  , sourceDnsDomain
  , sourceServiceName
  , sourceTranslatedAddress
  , sourceTranslatedPort
  , sourceProcessId
  , sourceUserPrivileges
  , sourceProcessName
  , sourcePort
  , sourceAddress
  , startTime
  , sourceUserId
  , sourceUserName
  ) where

--------------------------------------------------------------------------------
import           Data.ByteString.Builder
import           Data.Monoid
import qualified Data.Text               as T
import qualified Data.Text.Encoding      as T
import           Data.Time.Clock
import           Data.Time.Clock.POSIX
--------------------------------------------------------------------------------

newtype Extensions = Extensions { unExtensions :: Builder }

instance Monoid Extensions where
  mempty                                = emptyExtensions
  {-# INLINE mappend #-}
  Extensions e1 `mappend` Extensions e2 = Extensions (e1 <> " " <> e2)

emptyExtensions :: Extensions
emptyExtensions = Extensions mempty

-- |
-- >>> :set -XOverloadedStrings
-- >>> toLazyByteString $ extensionsBuilder (applicationProtocol "PUT" <> deviceCustomIPv6Address1 "localnet" "::1")
-- "app=PUT c6a1Label=localnet c6a1=::1"
extensionsBuilder :: Extensions -> Builder
extensionsBuilder = unExtensions

{-# INLINE ext #-}
ext :: T.Text -> Builder -> Extensions
ext k v = Extensions (T.encodeUtf8Builder k <> "=" <> v)

-- | See /Chapter 4: User-Defined Extensions/ from the reference
customExtension :: T.Text -> T.Text -> Extensions
customExtension k = ext k . renderExtVal

renderExtVal :: T.Text -> Builder
renderExtVal = T.encodeUtf8Builder . T.concatMap go
  where go '\\' = "\\\\"
        go '=' = "\\="
        go '\r' = "\\r"
        go '\n' = "\\n"
        go i = T.singleton i

type IPv6Address = T.Text
type IPv4Address = T.Text
type MACAddress  = T.Text
type TimeStamp   = UTCTime

utcTimeBuilder :: UTCTime -> Builder
utcTimeBuilder = intDec . fromInteger . round . (* 1000) . utcTimeToPOSIXSeconds

-- Max: 63 Character
deviceAction :: T.Text -> Extensions
deviceAction = customExtension "act"

-- Max: 31 Character
applicationProtocol :: T.Text -> Extensions
applicationProtocol = customExtension "app"

deviceCustomIPv6Address1, deviceCustomIPv6Address2, deviceCustomIPv6Address3, deviceCustomIPv6Address4
  :: T.Text -> IPv6Address -> Extensions
deviceCustomIPv6Address1 t a = customExtension "c6a1Label" t <> customExtension "c6a1" a
deviceCustomIPv6Address2 t a = customExtension "c6a2Label" t <> customExtension "c6a2" a
deviceCustomIPv6Address3 t a = customExtension "c6a3Label" t <> customExtension "c6a3" a
deviceCustomIPv6Address4 t a = customExtension "c6a4Label" t <> customExtension "c6a4" a

-- Max: 1023 Character
deviceEventCategory :: T.Text -> Extensions
deviceEventCategory = customExtension "cat"

deviceCustomFloatingPoint1, deviceCustomFloatingPoint2, deviceCustomFloatingPoint3, deviceCustomFloatingPoint4
  :: T.Text -> Double -> Extensions
deviceCustomFloatingPoint1 t a = customExtension "cfp1Label" t <> ext "cfp1" (doubleDec a)
deviceCustomFloatingPoint2 t a = customExtension "cfp2Label" t <> ext "cfp2" (doubleDec a)
deviceCustomFloatingPoint3 t a = customExtension "cfp3Label" t <> ext "cfp3" (doubleDec a)
deviceCustomFloatingPoint4 t a = customExtension "cfp4Label" t <> ext "cfp4" (doubleDec a)

-- Max: 1023 Character for labels
deviceCustomNumber1, deviceCustomNumber2, deviceCustomNumber3, deviceCustomNumber4
  :: T.Text -> Int -> Extensions
deviceCustomNumber1 t a = customExtension "cn1Label" t <> ext "cn1" (intDec a)
deviceCustomNumber2 t a = customExtension "cn2Label" t <> ext "cn2" (intDec a)
deviceCustomNumber3 t a = customExtension "cn3Label" t <> ext "cn3" (intDec a)
deviceCustomNumber4 t a = customExtension "cn4Label" t <> ext "cn4" (intDec a)

baseEventCount :: Int -> Extensions
baseEventCount = ext "cnt" . intDec

-- Max: 1023 Character for labels
deviceCustomString1, deviceCustomString2, deviceCustomString3, deviceCustomString4, deviceCustomString5, deviceCustomString6
  :: T.Text -> T.Text -> Extensions
deviceCustomString1 t a = customExtension "cs1Label" t <> customExtension "cs1" a
deviceCustomString2 t a = customExtension "cs2Label" t <> customExtension "cs2" a
deviceCustomString3 t a = customExtension "cs3Label" t <> customExtension "cs3" a
deviceCustomString4 t a = customExtension "cs4Label" t <> customExtension "cs4" a
deviceCustomString5 t a = customExtension "cs5Label" t <> customExtension "cs5" a
deviceCustomString6 t a = customExtension "cs6Label" t <> customExtension "cs6" a

destinationDnsDomain :: T.Text -> Extensions
destinationDnsDomain = customExtension "destinationDnsDomain"

destinationServiceName :: T.Text -> Extensions
destinationServiceName = customExtension "destinationServiceName"

destinationTranslatedAddress :: IPv4Address -> Extensions
destinationTranslatedAddress = customExtension "destinationTranslatedAddress"

destinationTranslatedPort :: Int -> Extensions
destinationTranslatedPort = ext "deviceTranslatedPort" . intDec

-- Max: 1023 Character for labels
deviceCustomDate1, deviceCustomDate2 :: T.Text -> TimeStamp -> Extensions
deviceCustomDate1 t a = customExtension "deviceCustomDate1Label" t <> ext "deviceCustomDate1" (utcTimeBuilder a)
deviceCustomDate2 t a = customExtension "deviceCustomDate2Label" t <> ext "deviceCustomDate2" (utcTimeBuilder a)

deviceDirectionInbound, deviceDirectionOutbound :: Extensions
deviceDirectionInbound  = ext "deviceDirection" (intDec 0)
deviceDirectionOutbound = ext "deviceDirection" (intDec 1)

-- Max: 255 Character
deviceDnsDomain :: T.Text -> Extensions
deviceDnsDomain = customExtension "deviceDnsDomain"

-- Max: 255 Character
deviceExternalId :: T.Text -> Extensions
deviceExternalId = customExtension "deviceExternalId"

-- Max: 1023 Character
deviceFacility :: T.Text -> Extensions
deviceFacility = customExtension "deviceFacility"

-- Max: 15 Character
deviceInboundInterface :: T.Text -> Extensions
deviceInboundInterface = customExtension "deviceInboundInterface"

deviceMacAddress :: MACAddress -> Extensions
deviceMacAddress = customExtension "deviceMacAddress"

-- Max: 255 Character
deviceNtDomain :: T.Text -> Extensions
deviceNtDomain = customExtension "deviceNtDomain"

-- Max: 15 Character
deviceOutboundInterface :: T.Text -> Extensions
deviceOutboundInterface = customExtension "deviceOutboundInterface"

-- Max: 1023 Character
deviceProcessName :: T.Text -> Extensions
deviceProcessName = customExtension "deviceProcessName"

deviceTranslatedAddress :: IPv4Address -> Extensions
deviceTranslatedAddress = customExtension "deviceTranslatedAddress"

-- Max: 1023 Character
destinationHostName :: T.Text -> Extensions
destinationHostName = customExtension "dhost"

destinationMacAddress :: MACAddress -> Extensions
destinationMacAddress = customExtension "dmac"

-- Max: 255 Character
destinationNtDomain :: T.Text -> Extensions
destinationNtDomain = customExtension "dntdom"

destinationProcessId :: T.Text -> Extensions
destinationProcessId = customExtension "dpid"

-- Max: 1023 Character
destinationUserPrivileges :: T.Text -> Extensions
destinationUserPrivileges = customExtension "dpriv"

-- Max: 1023 Character
destinationProcessName :: T.Text -> Extensions
destinationProcessName = customExtension "dproc"

destinationPort :: Int -> Extensions
destinationPort = ext "dpt" . intDec

destinationAddress :: IPv4Address -> Extensions
destinationAddress = customExtension "dst"

-- Max: 1023 Character
destinationUserId :: T.Text -> Extensions
destinationUserId = customExtension "duid"

-- Max: 1023 Character
destinationUserName :: T.Text -> Extensions
destinationUserName = customExtension "duser"

deviceAddress :: IPv4Address -> Extensions
deviceAddress = customExtension "dvc"

-- Max: 100 Character
deviceHostName :: T.Text -> Extensions
deviceHostName = customExtension "dvchost"

deviceProcessId :: T.Text -> Extensions
deviceProcessId = customExtension "dvcpid"

endTime :: TimeStamp -> Extensions
endTime = ext "end" . utcTimeBuilder

-- Max: 40 Character
externalId :: T.Text -> Extensions
externalId = customExtension "externalId"

fileCreateTime :: TimeStamp -> Extensions
fileCreateTime = ext "fileCreateTime" . utcTimeBuilder

-- Max: 255 Character
fileHash :: T.Text -> Extensions
fileHash = customExtension "fileHash"

-- Max: 1023 Character
fileId :: T.Text -> Extensions
fileId = customExtension "fileId"

fileModificationTime :: TimeStamp -> Extensions
fileModificationTime = ext "fileModificationTime" . utcTimeBuilder

-- Max: 1023 Character
filePath :: T.Text -> Extensions
filePath = customExtension "filePath"

-- Max: 1023 Character
filePermission :: T.Text -> Extensions
filePermission = customExtension "filePermission"

-- Max: 1023 Character
fileType :: T.Text -> Extensions
fileType = customExtension "fileType"

-- Max: 1023 Character
fileName :: T.Text -> Extensions
fileName = customExtension "fileName"

fileSize :: Int -> Extensions
fileSize = ext "fileSize" . intDec

bytesIn :: Int -> Extensions
bytesIn = ext "in" . intDec

-- Max: 1023 Character
message :: T.Text -> Extensions
message = customExtension "msg"

oldFileCreateTime :: TimeStamp -> Extensions
oldFileCreateTime = ext "oldFileCreateTime" . utcTimeBuilder

-- Max: 255 Character
oldFileHash :: T.Text -> Extensions
oldFileHash = customExtension "oldFileHash"

-- Max: 1023 Character
oldFileId :: T.Text -> Extensions
oldFileId = customExtension "oldFileId"

oldFileModificationTime :: TimeStamp -> Extensions
oldFileModificationTime = ext "oldFileModificationTime" . utcTimeBuilder

-- Max: 1023 Character
oldFileName :: T.Text -> Extensions
oldFileName = customExtension "oldFileName"

-- Max: 1023 Character
oldFilePath :: T.Text -> Extensions
oldFilePath = customExtension "oldFilePath"

-- Max: 1023 Character
oldFilePermission :: T.Text -> Extensions
oldFilePermission = customExtension "oldFilePermission"

-- Max: 1023 Character
oldFileSize :: Int -> Extensions
oldFileSize = ext "oldFileSize" . intDec

-- Max: 1023 Character
oldFileType :: T.Text -> Extensions
oldFileType = customExtension "oldFileType"

bytesOut :: Int -> Extensions
bytesOut = ext "out" . intDec
-- Max: 63 Character
eventOutcome :: T.Text -> Extensions
eventOutcome = customExtension "outcome"

transportProtocol :: T.Text -> Extensions
transportProtocol = customExtension "proto"

-- Max: 1023 Character
reason :: T.Text -> Extensions
reason = customExtension "reason"

-- Max: 1023 Character
requestURL :: T.Text -> Extensions
requestURL = customExtension "request"

-- Max: 1023 Character
requestClientApplication :: T.Text -> Extensions
requestClientApplication = customExtension "requestClientApplication"

-- Max: 1023 Character
requestCookies :: T.Text -> Extensions
requestCookies = customExtension "requestCookies"

-- Max: 1023 Character
requestMethod :: T.Text -> Extensions
requestMethod = customExtension "requestMethod"

receiptTime :: TimeStamp -> Extensions
receiptTime = ext "rt" . utcTimeBuilder

-- Max: 1023 Character
sourceHostName :: T.Text -> Extensions
sourceHostName = customExtension "shost"

sourceMacAddress :: MACAddress -> Extensions
sourceMacAddress = customExtension "smac"

-- Max: 255 Character
sourceNtDomain :: T.Text -> Extensions
sourceNtDomain = customExtension "sntdom"

-- Max: 255 Character
sourceDnsDomain :: T.Text -> Extensions
sourceDnsDomain = customExtension "sourceDnsDomain"

-- Max: 1023 Character
sourceServiceName :: T.Text -> Extensions
sourceServiceName = customExtension "sourceServiceName"

sourceTranslatedAddress :: IPv4Address -> Extensions
sourceTranslatedAddress = customExtension "sourceTranslatedAddress"

sourceTranslatedPort :: Int -> Extensions
sourceTranslatedPort = ext "sourceTranslatedPort" . intDec

sourceProcessId :: Int -> Extensions
sourceProcessId = ext "spid" . intDec

-- Max: 1023 Character
sourceUserPrivileges :: T.Text -> Extensions
sourceUserPrivileges = customExtension "spriv"

-- Max: 1023 Character
sourceProcessName :: T.Text -> Extensions
sourceProcessName = customExtension "sproc"

sourcePort :: Int -> Extensions
sourcePort = ext "spt" . intDec

sourceAddress :: IPv4Address -> Extensions
sourceAddress = customExtension "src"

startTime :: TimeStamp -> Extensions
startTime = ext "start" . utcTimeBuilder

-- Max: 1023 Character
sourceUserId :: T.Text -> Extensions
sourceUserId = customExtension "suid"

-- Max: 1023 Character
sourceUserName :: T.Text -> Extensions
sourceUserName = customExtension "suser"


-- TODO: enforce data size limits for each extension