{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE TypeSynonymInstances #-}

module HaskellWorks.Data.Network.Ip.SafeEnum
  ( SafeEnum(..)
  , boundedPred
  , boundedSucc
  ) where

import Data.Int
import Data.Maybe
import Data.Word
import HaskellWorks.Data.Network.Ip.Word128

class SafeEnum a where
  safePred :: a -> Maybe a
  safeSucc :: a -> Maybe a

instance SafeEnum Word where
  safePred = defaultSafePred
  safeSucc = defaultSafeSucc

instance SafeEnum Word8 where
  safePred = defaultSafePred
  safeSucc = defaultSafeSucc

instance SafeEnum Word16 where
  safePred = defaultSafePred
  safeSucc = defaultSafeSucc

instance SafeEnum Word32 where
  safePred = defaultSafePred
  safeSucc = defaultSafeSucc

instance SafeEnum Word64 where
  safePred = defaultSafePred
  safeSucc = defaultSafeSucc

instance SafeEnum Word128 where
  safePred = defaultSafePred
  safeSucc = defaultSafeSucc

instance SafeEnum Int where
  safePred = defaultSafePred
  safeSucc = defaultSafeSucc

instance SafeEnum Int8 where
  safePred = defaultSafePred
  safeSucc = defaultSafeSucc

instance SafeEnum Int16 where
  safePred = defaultSafePred
  safeSucc = defaultSafeSucc

instance SafeEnum Int32 where
  safePred = defaultSafePred
  safeSucc = defaultSafeSucc

instance SafeEnum Int64 where
  safePred = defaultSafePred
  safeSucc = defaultSafeSucc

instance SafeEnum Char where
  safePred = defaultSafePred
  safeSucc = defaultSafeSucc

instance SafeEnum Bool where
  safePred = defaultSafePred
  safeSucc = defaultSafeSucc

instance SafeEnum () where
  safePred = defaultSafePred
  safeSucc = defaultSafeSucc

instance SafeEnum Integer where
  safePred = Just . pred
  safeSucc = Just . succ

boundedPred :: SafeEnum a => a -> a
boundedPred a = fromMaybe a (safePred a)

boundedSucc :: SafeEnum a => a -> a
boundedSucc a = fromMaybe a (safeSucc a)

defaultSafePred :: (Bounded a, Enum a, Eq a) => a -> Maybe a
defaultSafePred v = if v /= minBound then Just (pred v) else Nothing

defaultSafeSucc :: (Bounded a, Enum a, Eq a) => a -> Maybe a
defaultSafeSucc v = if v /= maxBound then Just (succ v) else Nothing