{-# LANGUAGE GADTs, MultiParamTypeClasses, FunctionalDependencies  #-}

-------------------------------------------------------------------------------
-- |
-- Module      : Control.Monad.CC.Cursor
-- Copyright   : (c) Dan Doel
-- License     : MIT
--
-- Maintainer  : Dan Doel
-- Stability   : Experimental
-- Portability : Non-portable (Generalized algebraic data types,
--                             Functional Dependencies)
--
-- Implements various cursor datatypes for iterating over collections
module Control.Monad.CC.Cursor (
        Cursor(..),
        Iterator,
        generator,
        iterator,
        current,
        next,
        open,
        update,
--        Walkable(..),
--        Zipper,
--        zipper,
--        previousDir,
--        currentTerm,
--        move
    ) where

import Prelude hiding (zip, mapM, mapM_)
import Control.Monad hiding (mapM, mapM_)
import Control.Monad.CC

import Data.Maybe
import Data.Foldable
import Data.Traversable hiding (traverse)

-- | A generalized type that represents a reified data structure traversal.
-- The other traversal data types in this module are special cases of this
-- general type. Cursor is parameterized by four types:
--
-- m : The monad in which the Cursor object is usable.
--
-- r : The result type, which will be stored in the cursor once the traversal
--     has been completed.
--
-- b : The type that the cursor expects to receive before moving on to the
--     next element in the traversal.
--
-- a : The element type to which the Cursor provides access at each step in
--     the traversal.
data Cursor m r b a where
   Current :: Monad m => a -> (b -> m (Cursor m r b a)) -> Cursor m r b a
   Done    :: Monad m => r -> Cursor m r b a

-- | A simple iterator, which provides a way to view each of the elements of
-- a data structure in order.
type Iterator m a = Cursor m () () a

-- | A function for making a cursor out of a free form generator, similar to
-- using 'yield' in Ruby or Python. For example:
--
-- > generator $ \yield -> do a <- yield 1 ; yield 2 ; b <- yield 3 ; return [a,b]
generator :: MonadDelimitedCont p s m => ((a -> m b) -> m r) -> m (Cursor m r b a)
generator f = reset (\p -> Done `liftM` f (yield p))
 where yield p a = shift p (\k -> return $ Current a (k . return))

-- A general cursor builder; takes the traversal function, a data structure, and
-- returns a corresponding cursor. Currently not exported, just used internally.
makeCursor :: (MonadDelimitedCont p s m) =>
                ((a -> m b) -> t -> m r) -> t -> m (Cursor m r b a)
makeCursor iter t = generator $ flip iter t

-- | Creates an Iterator that will yield each of the elements of a Foldable in
-- order.
iterator :: (Foldable t, MonadDelimitedCont p s m) => t a -> m (Iterator m a)
iterator = makeCursor mapM_

-- | Advances an Iterator to the next element (has no effect on a finished Iterator).
next :: Iterator m a -> m (Iterator m a)
next = update ()

-- | Extracts the current element from a cursor, if applicable.
current :: Cursor m r b a -> Maybe a
current (Done _)      = Nothing
current (Current a _) = Just a

-- | Begins an updating traversal over a Traversable structure. At each step,
-- the cursor will hold an element of type a, and providing an element of type
-- b will move on to the next step. When done, a new Traversable object holding
-- elements of type b will be available.
open :: (Traversable t, MonadDelimitedCont p s m) => t a -> m (Cursor m (t b) b a)
open = makeCursor mapM

-- | Provides an item to a Cursor, moving on to the next step in the traversal.
-- (has no effect on a finished Cursor).
update :: b -> Cursor m r b a -> m (Cursor m r b a)
update _ c@(Done _)    = return c
update b (Current _ k) = k b

-- Removing for now. This isn't remotely done, and I need to reread ccshan's
-- stuff on zippers and such before I can begin to get it right.
{-
class Direction d where
    nextD :: d -> d

class Direction d => Walkable t d | t -> d where
    walk :: Monad m => (d -> t -> m (Maybe t, d)) -> t -> m t

data ListDir = LLeft | LRight

instance Direction ListDir where
    nextD = id

instance Walkable [a] ListDir where
    walk tr ll = fromMaybe ll `liftM` traverse LRight ll
     where
     traverse d l = do (ml, d') <- tr d l
                       let l' = fromMaybe l ml
                       maybe ml Just `liftM` select l' d'
     select _        LLeft  = return Nothing
     select l@(x:xs) LRight = do l' <- liftM (x:) `liftM` traverse LRight xs
                                 maybe l' Just `liftM` traverse LLeft (fromMaybe l l')
     select []       LRight = maybe Nothing Just `liftM` traverse LLeft []

type Zipper m t d = Cursor m t (Maybe t, d) (d,t)

zipper :: (MonadDelimitedCont p s m, Walkable t d) => t -> m (Zipper m t d)
zipper = makeCursor $ walk . curry

previousDir :: Zipper m t d -> Maybe d
previousDir (Done _)          = Nothing
previousDir (Current (d,_) _) = Just d

currentTerm :: Zipper m t d -> t
currentTerm (Done t)          = t
currentTerm (Current (_,t) _) = t

move :: d -> Zipper m t d -> m (Zipper m t d)
move _ z@(Done _) = return z
move d (Current _ k) = k (Nothing, d)
-}