{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Network.SSH.Exception where

import           Control.Exception              ( Exception )
import qualified Data.ByteString               as BS
import           Data.String
import           Data.Word

data Disconnect = Disconnect DisconnectParty DisconnectReason DisconnectMessage
    deriving (Eq, Ord, Show)

data DisconnectParty = Local | Remote
    deriving (Eq, Ord, Show)

data DisconnectReason
    = DisconnectHostNotAllowedToConnect
    | DisconnectProtocolError
    | DisconnectKeyExchangeFailed
    | DisconnectReserved
    | DisconnectMacError
    | DisconnectCompressionError
    | DisconnectServiceNotAvailable
    | DisconnectProtocolVersionNotSupported
    | DisconnectHostKeyNotVerifiable
    | DisconnectConnectionLost
    | DisconnectByApplication
    | DisconnectTooManyConnection
    | DisconnectAuthCancelledByUser
    | DisconnectNoMoreAuthMethodsAvailable
    | DisconnectIllegalUsername
    | DisconnectOtherReason Word32
    deriving (Eq, Ord, Show)

newtype DisconnectMessage = DisconnectMessage BS.ByteString
    deriving (Eq, Ord, Show, Semigroup, Monoid, IsString)

instance Exception Disconnect where

exceptionProtocolVersionNotSupported :: Disconnect
exceptionProtocolVersionNotSupported =
    Disconnect Local DisconnectProtocolVersionNotSupported mempty

exceptionConnectionLost :: Disconnect
exceptionConnectionLost =
    Disconnect Local DisconnectConnectionLost mempty

exceptionKexInvalidTransition :: Disconnect
exceptionKexInvalidTransition =
    Disconnect Local DisconnectKeyExchangeFailed "invalid transition"

exceptionKexInvalidSignature :: Disconnect
exceptionKexInvalidSignature =
    Disconnect Local DisconnectKeyExchangeFailed "invalid signature"

exceptionKexNoSignature :: Disconnect
exceptionKexNoSignature =
    Disconnect Local DisconnectKeyExchangeFailed "no signature"

exceptionKexNoCommonKexAlgorithm :: Disconnect
exceptionKexNoCommonKexAlgorithm =
    Disconnect Local DisconnectKeyExchangeFailed "no common kex algorithm"

exceptionKexNoCommonEncryptionAlgorithm :: Disconnect
exceptionKexNoCommonEncryptionAlgorithm =
    Disconnect Local DisconnectKeyExchangeFailed "no common encryption algorithm"

exceptionMacError :: Disconnect
exceptionMacError =
    Disconnect Local DisconnectMacError mempty

exceptionInvalidPacket :: Disconnect
exceptionInvalidPacket =
    Disconnect Local DisconnectProtocolError "invalid packet"

exceptionPacketLengthExceeded :: Disconnect
exceptionPacketLengthExceeded =
    Disconnect Local DisconnectProtocolError "packet length exceeded"

exceptionAuthenticationTimeout :: Disconnect
exceptionAuthenticationTimeout =
    Disconnect Local DisconnectByApplication "authentication timeout"

exceptionAuthenticationLimitExceeded :: Disconnect
exceptionAuthenticationLimitExceeded =
    Disconnect Local DisconnectByApplication "authentication limit exceeded"

exceptionServiceNotAvailable :: Disconnect
exceptionServiceNotAvailable =
    Disconnect Local DisconnectServiceNotAvailable mempty

exceptionInvalidChannelId :: Disconnect
exceptionInvalidChannelId =
    Disconnect Local DisconnectProtocolError "invalid channel id"

exceptionInvalidChannelRequest :: Disconnect
exceptionInvalidChannelRequest =
    Disconnect Local DisconnectProtocolError "invalid channel request"

exceptionWindowSizeOverflow :: Disconnect
exceptionWindowSizeOverflow =
    Disconnect Local DisconnectProtocolError "window size overflow"

exceptionWindowSizeUnderrun :: Disconnect
exceptionWindowSizeUnderrun =
    Disconnect Local DisconnectProtocolError "window size underrun"

exceptionPacketSizeExceeded :: Disconnect
exceptionPacketSizeExceeded =
    Disconnect Local DisconnectProtocolError "packet size exceeded"

exceptionDataAfterEof :: Disconnect
exceptionDataAfterEof =
    Disconnect Local DisconnectProtocolError "data after eof"

exceptionAlreadyExecuting :: Disconnect
exceptionAlreadyExecuting =
    Disconnect Local DisconnectProtocolError "already executing"

exceptionUnexpectedMessage :: BS.ByteString -> Disconnect
exceptionUnexpectedMessage raw
    | BS.null raw = Disconnect Local DisconnectProtocolError "empty message"
    | otherwise   = Disconnect Local DisconnectProtocolError msg
    where
        x   = BS.head raw
        x0  = (x `div` 100) + 48
        x1  = ((x `div` 10) `mod` 10) + 48
        x2  = (x `mod` 10) + 48
        msg = DisconnectMessage $ "unexpected message type " <> BS.pack [x0,x1,x2]