{-| This module exports the 'Plan' Applicative.

>>> :{
    let example :: Plan String [Int] IO () ()
        example = 
            step "a" (step "b" (foretell [1] *> plan (threadDelay 1e6)) 
                      *>
                      step "c" (foretell [2] *> plan (threadDelay 1e6))) 
            *>
            step "d" (step "e" (foretell [3] *> plan (threadDelay 1e6)) 
                      *>
                      step "f" (foretell [4] *> plan (threadDelay 1e6)))
    in 
    bifoldMap id (foldMap Prelude.show) (getSteps example)
    :}
"ab1c2de3f4"

Some possible use cases:

- Inspect the steps of an existing 'Plan' from @ghci@ using 'getSteps',
  'toForest' and 'Data.Tree.drawForest', as a form of documentation.

- If your script requires files that must be already present in the file
  system, use 'foretell' to annotate each 'Plan' action that requires a file,
  then get the global list of files using 'getSteps' and 'foldMap', and check
  that they all exist before running the 'Plan' with 'runPlan'.

- Get progress updates for your script by declaring (possibly nested) steps
  with 'step', running the 'Plan' with 'runPlan', and providing a notification
  callback with 'onTick', probably using 'completedness','toForest' and
  'Data.Tree.drawForest' to render the updates.

- Run a 'Plan' with 'runPlan', use 'instants' an 'toForest' on the resulting
  'Timeline' to get the durations of each step, then use 'zipSteps' on the same
  'Plan' and run it again. Now whenever a step finishes we can know if it took
  more or less than in the previous execution.

-}
module Control.Plan (
                    -- * Constructing plans
                      Plan
                    , plan
                    , plan'
                    , planIO
                    , planIO'
                    -- ** Declaring steps and annotations
                    , step
                    , skippable
                    , foretell
                    -- * Analyzing plans
                    , getSteps
                    , Steps
                    , mandatoriness
                    , Mandatoriness(..)
                    , foldSteps
                    -- * Adapting plans
                    -- $adapting
                    , bimapSteps
                    , zoomSteps
                    , zipSteps
                    , hoistPlan
                    -- * Running plans
                    , unliftPlan
                    , unliftPlan'
                    , runPlan
                    , runPlan'
                    , onTick
                    , Tick(..)
                    , completedness
                    , Context(..)
                    , Progress(..)
                    , Timeline
                    , instants
                    , foldTimeline
                    -- * The Sylvan typeclass
                    , Sylvan(..)
                    -- * Re-exports
                    , Data.Bifunctor.bimap
                    , Data.Bifoldable.bifoldMap
                    , Data.Bitraversable.bitraverse
                    , Control.Comonad.extract
                    -- $extract
                    , Streaming.hoist
                    , Streaming.Prelude.effects
                    -- $effects

                    -- * Deprecated functions
                    , kplan
                    , kplanIO
                    , unliftKPlan
                    , runKPlan
                    ) where

import Data.Bifunctor
import Data.Bifoldable
import Data.Bitraversable
import Control.Comonad
import Streaming
import Streaming.Prelude

import Control.Plan.Core

{- $setup

>>> :set -XNumDecimals
>>> :set -XArrows
>>> import Control.Applicative
>>> import Control.Plan
>>> import Control.Concurrent(threadDelay)

-}

{- $adapting

   Sometimes, we might need to mix 'Plan's for which step tags and annotations
   are of different types. These functions help with that.

-}

{- $extract
   Besides its usefulness with 'Timeline', 'extract' lets you get the head of a
   'NonEmpty' or the second element of a tuple.
-}

{- $effects
   'effects' lets you ignore all the update notifications while running a plan,
   when you are only interested in the final 'Timeline' and the result.
-}