module Data.SequenceId
       ( checkSeqIdM
       , checkSeqId
       , nextSeqIdM
       , nextSeqId
       , SequenceError (..)
       , SequenceIds (..)
       , SequenceId
       , SequenceT
       , evalSequenceT
       ) where


import           Control.Monad.Trans.State (StateT, evalStateT, gets, modify,
                                            put)
import           Data.Word                 (Word32)


newtype LastSeqId = LastSeqId { unLastSeqId :: SequenceId } deriving Show
type SequenceT = StateT LastSeqId
type SequenceId = Word32


evalSequenceT :: Monad m => SequenceId -> StateT LastSeqId m b -> m b
evalSequenceT = flip evalStateT . LastSeqId


data SequenceIds =
    SequenceIds
    { lastSeqId :: SequenceId
    , currSeqId :: SequenceId
    } deriving (Eq, Show)


data SequenceError
    = SequenceIdDropped SequenceIds
    | SequenceIdDuplicated SequenceIds
    deriving (Eq, Show)


checkSeqIdM :: Monad m => SequenceId -> (SequenceT m) (Maybe SequenceError)
checkSeqIdM currSeq = do
    lastSeq <- gets unLastSeqId
    put $ LastSeqId currSeq
    return $ checkSeqId lastSeq currSeq


checkSeqId :: SequenceId -> SequenceId -> Maybe SequenceError
checkSeqId lastSeq currSeq
    | (currSeq - lastSeq) > 1 = Just . SequenceIdDropped    $ SequenceIds lastSeq currSeq
    | (currSeq - lastSeq) < 1 = Just . SequenceIdDuplicated $ SequenceIds lastSeq currSeq
    | otherwise = Nothing


nextSeqIdM :: Monad m => SequenceT m SequenceId
nextSeqIdM = modify (LastSeqId . nextSeqId . unLastSeqId) >> gets unLastSeqId


nextSeqId :: SequenceId -> SequenceId
nextSeqId = (+1)