module Data.Order.Element.Representation (

    ElementRep (ElementRep),
    newMinimum,
    newMaximum,
    newAfter,
    newBefore

) where

-- Control

import Control.Monad.ST

-- Data

import           Data.Order.Representation
import           Data.Order.Algorithm.Raw (RawOrder, RawElement, RawAlgorithm)
import qualified Data.Order.Algorithm.Raw as Raw
import           Data.Order.Gate
import           Data.IORef

-- System

import System.IO.Unsafe

-- GHC

import GHC.IORef (IORef (IORef))

data ElementRep o e = ElementRep (RawAlgorithm RealWorld o e)
                                 (Gate o)
                                 (RawElement RealWorld e)
{-NOTE:
    When using OrderT, reduction of an ElementRep value to WHNF triggers the I/O
    for insertions.
-}

instance Eq (ElementRep o e) where

    ElementRep _ _ rawElem1  == ElementRep _ _ rawElem2 = rawElem1 == rawElem2

instance Ord (ElementRep o e) where

    compare (ElementRep rawAlg gate rawElem1)
            (ElementRep _      _    rawElem2) = ordering where
            
        ordering = unsafePerformIO $
                   withRawOrder gate $ \ rawOrder ->
                       stToIO $
                       Raw.compareElements rawAlg rawElem1 rawElem2 rawOrder

newMinimum :: OrderRep o e -> IO (ElementRep o e)
newMinimum = fromRawNew Raw.newMinimum

newMaximum :: OrderRep o e -> IO (ElementRep o e)
newMaximum = fromRawNew Raw.newMaximum

newAfter :: ElementRep o e -> OrderRep o e -> IO (ElementRep o e)
newAfter = fromRawNewNeighbor Raw.newAfter

newBefore :: ElementRep o e -> OrderRep o e -> IO (ElementRep o e)
newBefore = fromRawNewNeighbor Raw.newBefore

fromRawNewNeighbor :: (RawAlgorithm RealWorld o e ->
                       RawElement RealWorld e     ->
                       RawOrder RealWorld o       ->
                       ST RealWorld (RawElement RealWorld e))
                   -> ElementRep o e
                   -> OrderRep o e
                   -> IO (ElementRep o e)
fromRawNewNeighbor rawNewNeighbor (ElementRep _ _ rawElem) = fromRawNew rawNew where

    rawNew rawAlg = rawNewNeighbor rawAlg rawElem

fromRawNew :: (RawAlgorithm RealWorld o e ->
               RawOrder RealWorld o       ->
               ST RealWorld (RawElement RealWorld e))
           -> OrderRep o e
           -> IO (ElementRep o e)
fromRawNew rawNew (OrderRep rawAlg gate) = withRawOrder gate $ \ rawOrder -> do
    rawElem <- stToIO $ rawNew rawAlg rawOrder
    mkWeakIORef (IORef rawElem)
                (withRawOrder gate $ \ rawOrder ->
                     stToIO $
                     Raw.delete rawAlg rawElem rawOrder)
    return (ElementRep rawAlg gate rawElem)