-- Copyright 2013 Kevin Backhouse.

{-|
The 'DelayedLift' instrument is stateless and provides a similar
interface to 'readOnlyST2ToMP'. The difference is that it only
executes the read-only computation once the specified pass is reached.
-}

module Control.Monad.MultiPass.Instrument.DelayedLift
  ( DelayedLift
  , delayedLift, readST2ArrayMP
  )
where

import Control.Monad.ST2
import Control.Monad.MultiPass
import Data.Ix

-- | Abstract datatype for the instrument.
data DelayedLift r w p1 tc
  = DelayedLift
      { delayedLiftInternal ::
          !(forall a. p1 (ReadOnlyST2 r a) -> MultiPass r w tc (p1 a))
      }

-- | Execute the read-only computation during pass @p1@.
delayedLift
  :: Monad p1
  => DelayedLift r w p1 tc
  -> p1 (ReadOnlyST2 r a)
  -> MultiPass r w tc (p1 a)
delayedLift =
  delayedLiftInternal

instance Instrument tc () () (DelayedLift r w Off tc) where
  createInstrument _ _ () =
    wrapInstrument $ DelayedLift $ \Off ->
    return Off

instance Instrument tc () () (DelayedLift r w On tc) where
  createInstrument st2ToMP _ () =
    wrapInstrument $ DelayedLift $ \(On m) ->
    do x <- mkMultiPass $ st2ToMP $ runReadOnlyST2 m
       return (On x)

-- | 'readST2ArrayMP' is a simple application of 'delayedLift'. It
-- reads an index of the array during pass @p1@. This is particularly
-- useful if the array does not exist in earlier passes, for example
-- because it was created by the
-- 'Control.Monad.MultiPass.Instrument.CreateST2Array.CreateST2Array'
-- instrument.
readST2ArrayMP
  :: (Ix i, Monad p1)
  => DelayedLift r w p1 tc
  -> p1 (ST2Array r w i a)
  -> i
  -> MultiPass r w tc (p1 a)
readST2ArrayMP dlift xs i =
  delayedLift dlift $
  do xs' <- xs
     return (ReadOnlyST2 $ readST2Array xs' i)