    -- PacketClass.hs: OpenPGP (RFC4880) data types
    -- Copyright © 2012-2018  Clint Adams
    -- This software is released under the terms of the Expat license.
    -- (See the LICENSE file).
    {-# LANGUAGE DataKinds #-}
    {-# LANGUAGE DeriveDataTypeable #-}
    {-# LANGUAGE DeriveGeneric #-}
    {-# LANGUAGE ExistentialQuantification #-}
    {-# LANGUAGE GeneralizedNewtypeDeriving #-}
    {-# LANGUAGE TemplateHaskell #-}
    {-# LANGUAGE TypeFamilies #-}

    module Codec.Encryption.OpenPGP.Types.Internal.PacketClass where

    import Codec.Encryption.OpenPGP.Types.Internal.Base
    import Codec.Encryption.OpenPGP.Types.Internal.PKITypes
    import Codec.Encryption.OpenPGP.Types.Internal.Pkt

    import Control.Lens (makeLenses)
    import Data.ByteString.Lazy (ByteString)
    import qualified Data.ByteString.Lazy as BL
    import Data.Data (Data)
    import Data.List.NonEmpty (NonEmpty)
    import Data.Text (Text)
    import Data.Text.Prettyprint.Doc (Pretty(..))
    import Data.Typeable (Typeable)
    import Data.Word (Word8)

    class Packet a where
      data PacketType a :: *
      packetType :: a -> PacketType a
      packetCode :: PacketType a -> Word8
      toPkt :: a -> Pkt
      fromPkt :: Pkt -> a

    data PKESK =
      PKESK
        { _pkeskPacketVersion :: PacketVersion
        , _pkeskEightOctetKeyId :: EightOctetKeyId
        , _pkeskPubKeyAlgorithm :: PubKeyAlgorithm
        , _pkeskMPIs :: NonEmpty MPI
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet PKESK where
      data PacketType PKESK = PKESKType
                              deriving (Show, Eq)
      packetType _ = PKESKType
      packetCode _ = 1
      toPkt (PKESK a b c d) = PKESKPkt a b c d
      fromPkt (PKESKPkt a b c d) = PKESK a b c d
      fromPkt _ = error "Cannot coerce non-PKESK packet"

    instance Pretty PKESK where
      pretty = pretty . toPkt

    newtype Signature =
      Signature -- FIXME?
        { _signaturePayload :: SignaturePayload
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet Signature where
      data PacketType Signature = SignatureType
                                  deriving (Show, Eq)
      packetType _ = SignatureType
      packetCode _ = 2
      toPkt (Signature a) = SignaturePkt a
      fromPkt (SignaturePkt a) = Signature a
      fromPkt _ = error "Cannot coerce non-Signature packet"

    instance Pretty Signature where
      pretty = pretty . toPkt

    data SKESK =
      SKESK
        { _skeskPacketVersion :: PacketVersion
        , _skeskSymmetricAlgorithm :: SymmetricAlgorithm
        , _skeskS2K :: S2K
        , _skeskESK :: Maybe BL.ByteString
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet SKESK where
      data PacketType SKESK = SKESKType
                              deriving (Show, Eq)
      packetType _ = SKESKType
      packetCode _ = 3
      toPkt (SKESK a b c d) = SKESKPkt a b c d
      fromPkt (SKESKPkt a b c d) = SKESK a b c d
      fromPkt _ = error "Cannot coerce non-SKESK packet"

    instance Pretty SKESK where
      pretty = pretty . toPkt

    data OnePassSignature =
      OnePassSignature
        { _onePassSignaturePacketVersion :: PacketVersion
        , _onePassSignatureSigType :: SigType
        , _onePassSignatureHashAlgorithm :: HashAlgorithm
        , _onePassSignaturePubKeyAlgorithm :: PubKeyAlgorithm
        , _onePassSignatureEightOctetKeyId :: EightOctetKeyId
        , _onePassSignatureNestedFlag :: NestedFlag
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet OnePassSignature where
      data PacketType OnePassSignature = OnePassSignatureType
                                         deriving (Show, Eq)
      packetType _ = OnePassSignatureType
      packetCode _ = 4
      toPkt (OnePassSignature a b c d e f) = OnePassSignaturePkt a b c d e f
      fromPkt (OnePassSignaturePkt a b c d e f) = OnePassSignature a b c d e f
      fromPkt _ = error "Cannot coerce non-OnePassSignature packet"

    instance Pretty OnePassSignature where
      pretty = pretty . toPkt

    data SecretKey =
      SecretKey
        { _secretKeyPKPayload :: PKPayload
        , _secretKeySKAddendum :: SKAddendum
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet SecretKey where
      data PacketType SecretKey = SecretKeyType
                                  deriving (Show, Eq)
      packetType _ = SecretKeyType
      packetCode _ = 5
      toPkt (SecretKey a b) = SecretKeyPkt a b
      fromPkt (SecretKeyPkt a b) = SecretKey a b
      fromPkt _ = error "Cannot coerce non-SecretKey packet"

    instance Pretty SecretKey where
      pretty = pretty . toPkt

    newtype PublicKey =
      PublicKey
        { _publicKeyPKPayload :: PKPayload
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet PublicKey where
      data PacketType PublicKey = PublicKeyType
                                  deriving (Show, Eq)
      packetType _ = PublicKeyType
      packetCode _ = 6
      toPkt (PublicKey a) = PublicKeyPkt a
      fromPkt (PublicKeyPkt a) = PublicKey a
      fromPkt _ = error "Cannot coerce non-PublicKey packet"

    instance Pretty PublicKey where
      pretty = pretty . toPkt

    data SecretSubkey =
      SecretSubkey
        { _secretSubkeyPKPayload :: PKPayload
        , _secretSubkeySKAddendum :: SKAddendum
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet SecretSubkey where
      data PacketType SecretSubkey = SecretSubkeyType
                                     deriving (Show, Eq)
      packetType _ = SecretSubkeyType
      packetCode _ = 7
      toPkt (SecretSubkey a b) = SecretSubkeyPkt a b
      fromPkt (SecretSubkeyPkt a b) = SecretSubkey a b
      fromPkt _ = error "Cannot coerce non-SecretSubkey packet"

    instance Pretty SecretSubkey where
      pretty = pretty . toPkt

    data CompressedData =
      CompressedData
        { _compressedDataCompressionAlgorithm :: CompressionAlgorithm
        , _compressedDataPayload :: CompressedDataPayload
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet CompressedData where
      data PacketType CompressedData = CompressedDataType
                                       deriving (Show, Eq)
      packetType _ = CompressedDataType
      packetCode _ = 8
      toPkt (CompressedData a b) = CompressedDataPkt a b
      fromPkt (CompressedDataPkt a b) = CompressedData a b
      fromPkt _ = error "Cannot coerce non-CompressedData packet"

    instance Pretty CompressedData where
      pretty = pretty . toPkt

    newtype SymEncData =
      SymEncData
        { _symEncDataPayload :: ByteString
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet SymEncData where
      data PacketType SymEncData = SymEncDataType
                                   deriving (Show, Eq)
      packetType _ = SymEncDataType
      packetCode _ = 9
      toPkt (SymEncData a) = SymEncDataPkt a
      fromPkt (SymEncDataPkt a) = SymEncData a
      fromPkt _ = error "Cannot coerce non-SymEncData packet"

    instance Pretty SymEncData where
      pretty = pretty . toPkt

    newtype Marker =
      Marker
        { _markerPayload :: ByteString
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet Marker where
      data PacketType Marker = MarkerType
                               deriving (Show, Eq)
      packetType _ = MarkerType
      packetCode _ = 10
      toPkt (Marker a) = MarkerPkt a
      fromPkt (MarkerPkt a) = Marker a
      fromPkt _ = error "Cannot coerce non-Marker packet"

    instance Pretty Marker where
      pretty = pretty . toPkt

    data LiteralData =
      LiteralData
        { _literalDataDataType :: DataType
        , _literalDataFileName :: FileName
        , _literalDataTimeStamp :: ThirtyTwoBitTimeStamp
        , _literalDataPayload :: ByteString
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet LiteralData where
      data PacketType LiteralData = LiteralDataType
                                    deriving (Show, Eq)
      packetType _ = LiteralDataType
      packetCode _ = 11
      toPkt (LiteralData a b c d) = LiteralDataPkt a b c d
      fromPkt (LiteralDataPkt a b c d) = LiteralData a b c d
      fromPkt _ = error "Cannot coerce non-LiteralData packet"

    instance Pretty LiteralData where
      pretty = pretty . toPkt

    newtype Trust =
      Trust
        { _trustPayload :: ByteString
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet Trust where
      data PacketType Trust = TrustType
                              deriving (Show, Eq)
      packetType _ = TrustType
      packetCode _ = 12
      toPkt (Trust a) = TrustPkt a
      fromPkt (TrustPkt a) = Trust a
      fromPkt _ = error "Cannot coerce non-Trust packet"

    instance Pretty Trust where
      pretty = pretty . toPkt

    newtype UserId =
      UserId
        { _userIdPayload :: Text
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet UserId where
      data PacketType UserId = UserIdType
                               deriving (Show, Eq)
      packetType _ = UserIdType
      packetCode _ = 13
      toPkt (UserId a) = UserIdPkt a
      fromPkt (UserIdPkt a) = UserId a
      fromPkt _ = error "Cannot coerce non-UserId packet"

    instance Pretty UserId where
      pretty = pretty . toPkt

    newtype PublicSubkey =
      PublicSubkey
        { _publicSubkeyPKPayload :: PKPayload
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet PublicSubkey where
      data PacketType PublicSubkey = PublicSubkeyType
                                     deriving (Show, Eq)
      packetType _ = PublicSubkeyType
      packetCode _ = 14
      toPkt (PublicSubkey a) = PublicSubkeyPkt a
      fromPkt (PublicSubkeyPkt a) = PublicSubkey a
      fromPkt _ = error "Cannot coerce non-PublicSubkey packet"

    instance Pretty PublicSubkey where
      pretty = pretty . toPkt

    newtype UserAttribute =
      UserAttribute
        { _userAttributeSubPackets :: [UserAttrSubPacket]
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet UserAttribute where
      data PacketType UserAttribute = UserAttributeType
                                      deriving (Show, Eq)
      packetType _ = UserAttributeType
      packetCode _ = 17
      toPkt (UserAttribute a) = UserAttributePkt a
      fromPkt (UserAttributePkt a) = UserAttribute a
      fromPkt _ = error "Cannot coerce non-UserAttribute packet"

    instance Pretty UserAttribute where
      pretty = pretty . toPkt

    data SymEncIntegrityProtectedData =
      SymEncIntegrityProtectedData
        { _symEncIntegrityProtectedDataPacketVersion :: PacketVersion
        , _symEncIntegrityProtectedDataPayload :: ByteString
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet SymEncIntegrityProtectedData where
      data PacketType
           SymEncIntegrityProtectedData = SymEncIntegrityProtectedDataType
                                            deriving (Show, Eq)
      packetType _ = SymEncIntegrityProtectedDataType
      packetCode _ = 18
      toPkt (SymEncIntegrityProtectedData a b) = SymEncIntegrityProtectedDataPkt a b
      fromPkt (SymEncIntegrityProtectedDataPkt a b) =
        SymEncIntegrityProtectedData a b
      fromPkt _ = error "Cannot coerce non-SymEncIntegrityProtectedData packet"

    instance Pretty SymEncIntegrityProtectedData where
      pretty = pretty . toPkt

    newtype ModificationDetectionCode =
      ModificationDetectionCode
        { _modificationDetectionCodePayload :: ByteString
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet ModificationDetectionCode where
      data PacketType
           ModificationDetectionCode = ModificationDetectionCodeType
                                         deriving (Show, Eq)
      packetType _ = ModificationDetectionCodeType
      packetCode _ = 19
      toPkt (ModificationDetectionCode a) = ModificationDetectionCodePkt a
      fromPkt (ModificationDetectionCodePkt a) = ModificationDetectionCode a
      fromPkt _ = error "Cannot coerce non-ModificationDetectionCode packet"

    instance Pretty ModificationDetectionCode where
      pretty = pretty . toPkt

    data OtherPacket =
      OtherPacket
        { _otherPacketType :: Word8
        , _otherPacketPayload :: ByteString
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet OtherPacket where
      data PacketType OtherPacket = OtherPacketType
                                    deriving (Show, Eq)
      packetType _ = OtherPacketType
      packetCode _ = undefined -- FIXME
      toPkt (OtherPacket a b) = OtherPacketPkt a b
      fromPkt (OtherPacketPkt a b) = OtherPacket a b
      fromPkt _ = error "Cannot coerce non-OtherPacket packet"

    instance Pretty OtherPacket where
      pretty = pretty . toPkt

    data BrokenPacket =
      BrokenPacket
        { _brokenPacketParseError :: String
        , _brokenPacketType :: Word8
        , _brokenPacketPayload :: ByteString
        }
      deriving (Data, Eq, Show, Typeable)

    instance Packet BrokenPacket where
      data PacketType BrokenPacket = BrokenPacketType
                                     deriving (Show, Eq)
      packetType _ = BrokenPacketType
      packetCode _ = undefined
      toPkt (BrokenPacket a b c) = BrokenPacketPkt a b c
      fromPkt (BrokenPacketPkt a b c) = BrokenPacket a b c
      fromPkt _ = error "Cannot coerce non-BrokenPacket packet"

    instance Pretty BrokenPacket where
      pretty = pretty . toPkt

    $(makeLenses ''PKESK)

    $(makeLenses ''Signature)

    $(makeLenses ''SKESK)

    $(makeLenses ''OnePassSignature)

    $(makeLenses ''SecretKey)

    $(makeLenses ''PKPayload)

    $(makeLenses ''PublicKey)

    $(makeLenses ''SecretSubkey)

    $(makeLenses ''CompressedData)

    $(makeLenses ''SymEncData)

    $(makeLenses ''Marker)

    $(makeLenses ''LiteralData)

    $(makeLenses ''Trust)

    $(makeLenses ''UserId)

    $(makeLenses ''PublicSubkey)

    $(makeLenses ''UserAttribute)

    $(makeLenses ''SymEncIntegrityProtectedData)

    $(makeLenses ''ModificationDetectionCode)

    $(makeLenses ''OtherPacket)

    $(makeLenses ''BrokenPacket)