{-# LANGUAGE BangPatterns        #-}
{-# LANGUAGE DeriveGeneric       #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE RecordWildCards     #-}
{-# LANGUAGE TypeOperators       #-}

module VFlow.Types.SFlow
  ( SFlow(..)
  , ExtRouter(..)
  , ExtSwitch(..)
  , L2(..)
  , L3(..)
  , L4(..)
  , RawHeader(..)
  , Records(..)
  , SamplesElt(..)
  ) where

import Control.Monad (mzero)
import Data.Aeson (Value(..), FromJSON(..), ToJSON(..), pairs, (.:), (.=), object)
import Net.Types (IPv4, Mac)
import qualified GHC.Generics

data ExtRouter = ExtRouter {
    extRouterNextHop :: !IPv4,
    extRouterDstMask :: !Int,
    extRouterSrcMask :: !Int
  } deriving (Show,Eq,GHC.Generics.Generic)


instance FromJSON ExtRouter where
  parseJSON (Object v) = ExtRouter <$> v .:  "NextHop" <*> v .:  "DstMask" <*> v .:  "SrcMask"
  parseJSON _          = mzero


instance ToJSON ExtRouter where
  toJSON     (ExtRouter {..}) = object ["NextHop" .= extRouterNextHop, "DstMask" .= extRouterDstMask, "SrcMask" .= extRouterSrcMask]
  toEncoding (ExtRouter {..}) = pairs  ("NextHop" .= extRouterNextHop<>"DstMask" .= extRouterDstMask<>"SrcMask" .= extRouterSrcMask)


data ExtSwitch = ExtSwitch {
    extSwitchDstPriority :: !Int,
    extSwitchSrcVlan :: !Int,
    extSwitchSrcPriority :: !Int,
    extSwitchDstVlan :: !Int
  } deriving (Show,Eq,GHC.Generics.Generic)


instance FromJSON ExtSwitch where
  parseJSON (Object v) = ExtSwitch <$> v .:  "DstPriority" <*> v .:  "SrcVlan" <*> v .:  "SrcPriority" <*> v .:  "DstVlan"
  parseJSON _          = mzero


instance ToJSON ExtSwitch where
  toJSON     (ExtSwitch {..}) = object ["DstPriority" .= extSwitchDstPriority, "SrcVlan" .= extSwitchSrcVlan, "SrcPriority" .= extSwitchSrcPriority, "DstVlan" .= extSwitchDstVlan]
  toEncoding (ExtSwitch {..}) = pairs  ("DstPriority" .= extSwitchDstPriority<>"SrcVlan" .= extSwitchSrcVlan<>"SrcPriority" .= extSwitchSrcPriority<>"DstVlan" .= extSwitchDstVlan)


data L2 = L2 {
    l2Vlan :: !Int,
    l2EtherType :: !Int,
    l2DstMAC :: !Mac,
    l2SrcMAC :: !Mac
  } deriving (Show,Eq,GHC.Generics.Generic)


instance FromJSON L2 where
  parseJSON (Object v) = L2 <$> v .:  "Vlan" <*> v .:  "EtherType" <*> v .:  "DstMAC" <*> v .:  "SrcMAC"
  parseJSON _          = mzero


instance ToJSON L2 where
  toJSON     (L2 {..}) = object ["Vlan" .= l2Vlan, "EtherType" .= l2EtherType, "DstMAC" .= l2DstMAC, "SrcMAC" .= l2SrcMAC]
  toEncoding (L2 {..}) = pairs  ("Vlan" .= l2Vlan<>"EtherType" .= l2EtherType<>"DstMAC" .= l2DstMAC<>"SrcMAC" .= l2SrcMAC)


data L3 = L3 {
    l3TTL :: !Int,
    l3Flags :: !Int,
    l3TotalLen :: !Int,
    l3Checksum :: !Int,
    l3TOS :: !Int,
    l3Dst :: !IPv4,
    l3Protocol :: !Int,
    l3Src :: !IPv4,
    l3Version :: !Int,
    l3ID :: !Int,
    l3FragOff :: !Int
  } deriving (Show,Eq,GHC.Generics.Generic)


instance FromJSON L3 where
  parseJSON (Object v) = L3 <$> v .:  "TTL" <*> v .:  "Flags" <*> v .:  "TotalLen" <*> v .:  "Checksum" <*> v .:  "TOS" <*> v .:  "Dst" <*> v .:  "Protocol" <*> v .:  "Src" <*> v .:  "Version" <*> v .:  "ID" <*> v .:  "FragOff"
  parseJSON _          = mzero


instance ToJSON L3 where
  toJSON     (L3 {..}) = object ["TTL" .= l3TTL, "Flags" .= l3Flags, "TotalLen" .= l3TotalLen, "Checksum" .= l3Checksum, "TOS" .= l3TOS, "Dst" .= l3Dst, "Protocol" .= l3Protocol, "Src" .= l3Src, "Version" .= l3Version, "ID" .= l3ID, "FragOff" .= l3FragOff]
  toEncoding (L3 {..}) = pairs  ("TTL" .= l3TTL<>"Flags" .= l3Flags<>"TotalLen" .= l3TotalLen<>"Checksum" .= l3Checksum<>"TOS" .= l3TOS<>"Dst" .= l3Dst<>"Protocol" .= l3Protocol<>"Src" .= l3Src<>"Version" .= l3Version<>"ID" .= l3ID<>"FragOff" .= l3FragOff)


data L4 = L4 {
    l4Flags :: !Int,
    l4DataOffset :: !Int,
    l4SrcPort :: !Int,
    l4Reserved :: !Int,
    l4DstPort :: !Int
  } deriving (Show,Eq,GHC.Generics.Generic)


instance FromJSON L4 where
  parseJSON (Object v) = L4 <$> v .:  "Flags" <*> v .:  "DataOffset" <*> v .:  "SrcPort" <*> v .:  "Reserved" <*> v .:  "DstPort"
  parseJSON _          = mzero


instance ToJSON L4 where
  toJSON     (L4 {..}) = object ["Flags" .= l4Flags, "DataOffset" .= l4DataOffset, "SrcPort" .= l4SrcPort, "Reserved" .= l4Reserved, "DstPort" .= l4DstPort]
  toEncoding (L4 {..}) = pairs  ("Flags" .= l4Flags<>"DataOffset" .= l4DataOffset<>"SrcPort" .= l4SrcPort<>"Reserved" .= l4Reserved<>"DstPort" .= l4DstPort)


data RawHeader = RawHeader {
    rawHeaderL2 :: !L2,
    rawHeaderL3 :: !L3,
    rawHeaderL4 :: !L4
  } deriving (Show,Eq,GHC.Generics.Generic)


instance FromJSON RawHeader where
  parseJSON (Object v) = RawHeader <$> v .:  "L2" <*> v .:  "L3" <*> v .:  "L4"
  parseJSON _          = mzero


instance ToJSON RawHeader where
  toJSON     (RawHeader {..}) = object ["L2" .= rawHeaderL2, "L3" .= rawHeaderL3, "L4" .= rawHeaderL4]
  toEncoding (RawHeader {..}) = pairs  ("L2" .= rawHeaderL2<>"L3" .= rawHeaderL3<>"L4" .= rawHeaderL4)


data Records = Records {
    recordsExtRouter :: !ExtRouter,
    recordsExtSwitch :: !ExtSwitch,
    recordsRawHeader :: !RawHeader
  } deriving (Show,Eq,GHC.Generics.Generic)


instance FromJSON Records where
  parseJSON (Object v) = Records <$> v .:  "ExtRouter" <*> v .:  "ExtSwitch" <*> v .:  "RawHeader"
  parseJSON _          = mzero


instance ToJSON Records where
  toJSON     (Records {..}) = object ["ExtRouter" .= recordsExtRouter, "ExtSwitch" .= recordsExtSwitch, "RawHeader" .= recordsRawHeader]
  toEncoding (Records {..}) = pairs  ("ExtRouter" .= recordsExtRouter<>"ExtSwitch" .= recordsExtSwitch<>"RawHeader" .= recordsRawHeader)


data SamplesElt = SamplesElt {
    samplesEltDrops :: !Int,
    samplesEltSourceID :: !Int,
    samplesEltRecords :: !Records,
    samplesEltInput :: !Int,
    samplesEltSequenceNo :: !Int,
    samplesEltSamplingRate :: !Int,
    samplesEltOutput :: !Int,
    samplesEltRecordsNo :: !Int,
    samplesEltSamplePool :: !Int
  } deriving (Show,Eq,GHC.Generics.Generic)


instance FromJSON SamplesElt where
  parseJSON (Object v) = SamplesElt <$> v .:  "Drops" <*> v .:  "SourceID" <*> v .:  "Records" <*> v .:  "Input" <*> v .:  "SequenceNo" <*> v .:  "SamplingRate" <*> v .:  "Output" <*> v .:  "RecordsNo" <*> v .:  "SamplePool"
  parseJSON _          = mzero


instance ToJSON SamplesElt where
  toJSON     (SamplesElt {..}) = object ["Drops" .= samplesEltDrops, "SourceID" .= samplesEltSourceID, "Records" .= samplesEltRecords, "Input" .= samplesEltInput, "SequenceNo" .= samplesEltSequenceNo, "SamplingRate" .= samplesEltSamplingRate, "Output" .= samplesEltOutput, "RecordsNo" .= samplesEltRecordsNo, "SamplePool" .= samplesEltSamplePool]
  toEncoding (SamplesElt {..}) = pairs  ("Drops" .= samplesEltDrops<>"SourceID" .= samplesEltSourceID<>"Records" .= samplesEltRecords<>"Input" .= samplesEltInput<>"SequenceNo" .= samplesEltSequenceNo<>"SamplingRate" .= samplesEltSamplingRate<>"Output" .= samplesEltOutput<>"RecordsNo" .= samplesEltRecordsNo<>"SamplePool" .= samplesEltSamplePool)


data SFlow = SFlow {
    sflowIPAddress :: !IPv4,
    sflowAgentSubID :: !Int,
    sflowIPVersion :: !Int,
    sflowSequenceNo :: !Int,
    sflowSysUpTime :: !Int,
    sflowSamplesNo :: !Int,
    sflowVersion :: !Int,
    sflowSamples :: [SamplesElt]
  } deriving (Show,Eq,GHC.Generics.Generic)


instance FromJSON SFlow where
  parseJSON (Object v) = SFlow <$> v .:  "IPAddress" <*> v .:  "AgentSubID" <*> v .:  "IPVersion" <*> v .:  "SequenceNo" <*> v .:  "SysUpTime" <*> v .:  "SamplesNo" <*> v .:  "Version" <*> v .:  "Samples"
  parseJSON _          = mzero


instance ToJSON SFlow where
  toJSON     (SFlow {..}) = object ["IPAddress" .= sflowIPAddress, "AgentSubID" .= sflowAgentSubID, "IPVersion" .= sflowIPVersion, "SequenceNo" .= sflowSequenceNo, "SysUpTime" .= sflowSysUpTime, "SamplesNo" .= sflowSamplesNo, "Version" .= sflowVersion, "Samples" .= sflowSamples]
  toEncoding (SFlow {..}) = pairs  ("IPAddress" .= sflowIPAddress<>"AgentSubID" .= sflowAgentSubID<>"IPVersion" .= sflowIPVersion<>"SequenceNo" .= sflowSequenceNo<>"SysUpTime" .= sflowSysUpTime<>"SamplesNo" .= sflowSamplesNo<>"Version" .= sflowVersion<>"Samples" .= sflowSamples)