{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}

module CRDT.Cm.Counter
    ( Counter (..)
    , CounterOp (..)
    , initial
    ) where

import           Algebra.PartialOrd (PartialOrd (..))
import           Data.Observe (Observe (..))

import           CRDT.Cm (CmRDT (..))

newtype Counter a = Counter a
    deriving (Show)

data CounterOp a = Increment | Decrement
    deriving (Bounded, Enum, Eq, Show)

instance (Num a, Eq a) => CmRDT (Counter a) (CounterOp a) (CounterOp a) where
    updateAtSource = pure
    updateDownstream = opToFunc

instance Observe (Counter a) where
    type Observed (Counter a) = a
    observe (Counter c) = c

-- | Empty order, allowing arbitrary reordering
instance PartialOrd (CounterOp a) where
    leq _ _ = False

initial :: Num a => Counter a
initial = Counter 0

opToFunc :: Num a => CounterOp a -> Counter a -> Counter a
opToFunc op (Counter c) =
    Counter $ case op of
        Increment -> c + 1
        Decrement -> c - 1