module Control.Monad.Trans.Order.Lazy (

    -- * The Order monad

    Order,
    evalOrder,
    evalOrderWith,

    -- * The OrderT monad transformer

    OrderT,
    evalOrderT,
    evalOrderTWith,
    force,

    -- * Elements

    newMinimum,
    newMaximum,
    newAfter,
    newBefore

) where

-- Control

import Control.Monad.Trans.State.Lazy
import Control.Monad.Trans.Order.Lazy.Type

-- Data

import           Data.Functor.Identity
import           Data.Order.Algorithm
import           Data.Order.Algorithm.Type
import           Data.Order.Internals
                 hiding (newMinimum, newMaximum, newAfter, newBefore)
import qualified Data.Order.Internals as Internals
import           Data.Order.Raw (RawAlgorithm)

-- System

import System.IO.Unsafe

{-FIXME:
    Introduce conversions between the lazy and the strict variant, similar to
    the conversions for ST.
-}
{-FIXME:
    Consider introducing a restricted variant of mapStateT (for the lazy and the
    strict OrderT monad):

            mapOrderT :: (forall a . m a -> n a) -> OrderT o m a -> OrderT o n a

    Maybe this should not be called mapOrderT, since it is only a restricted
    variant and a corresponding mapOrder would be trivial.
-}
{-FIXME:
    Probably we should also have variants of liftCallCC, etc., which are present
    for StateT (for the lazy and the strict OrderT monad).
-}

-- * The Order monad

type Order o = OrderT o Identity

evalOrder :: (forall o . Order o a) -> a
evalOrder order = runIdentity (evalOrderT order)

evalOrderWith :: Algorithm -> (forall o . Order o a) -> a
evalOrderWith alg order = runIdentity (evalOrderTWith alg order)

-- * The OrderT monad transformer

-- NOTE: OrderT is imported from Control.Monad.Trans.Order.Lazy.Type.

evalOrderT :: Monad m => (forall o . OrderT o m a) -> m a
evalOrderT = evalOrderTWith defaultAlgorithm

evalOrderTWith :: Monad m => Algorithm -> (forall o . OrderT o m a) -> m a
evalOrderTWith (Algorithm rawAlg) (OrderT stateT) = monad where

    monad = evalStateT stateT (localOrderRep rawAlg)

force :: Monad m => OrderT o m ()
force = OrderT $ get >>= \ order -> order `seq` return ()

-- * Elements

newMinimum :: Monad m => OrderT o m (Element o)
newMinimum = fromRepNew Internals.newMinimum

newMaximum :: Monad m => OrderT o m (Element o)
newMaximum = fromRepNew Internals.newMaximum

newAfter :: Monad m => Element o -> OrderT o m (Element o)
newAfter elem = fromRepNew (Internals.newAfter elem)

newBefore :: Monad m => Element o -> OrderT o m (Element o)
newBefore elem = fromRepNew (Internals.newBefore elem)

fromRepNew :: Monad m
           => (OrderRep o -> IO (Element o))
           -> OrderT o m (Element o)
fromRepNew repNew = OrderT $ state statefulNew where

    statefulNew orderRep = (elem, elem `seq` orderRep) where

        {-# NOINLINE elem #-}
        elem = unsafePerformIO $ repNew orderRep