{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE TypeSynonymInstances       #-}
{-# OPTIONS -Wall #-}

--------------------------------------------------------------------------------
-- |
-- Module      :  Wumpus.Basic.Monads.TurtleMonad
-- Copyright   :  (c) Stephen Tetley 2010
-- License     :  BSD3
--
-- Maintainer  :  stephen.tetley@gmail.com
-- Stability   :  unstable
-- Portability :  GHC 
--
-- Turtle monad and monad transformer.
--
-- The Turtle monad embodies the LOGO style of imperative 
-- drawing - sending commands to update the a cursor.
--
-- While Wumpus generally aims for a more compositional,
-- \"coordinate-free\" style of drawing, some types of 
-- diagram are very easily expressed in the LOGO style.
--
--------------------------------------------------------------------------------

module Wumpus.Basic.Monads.TurtleMonad
  (
    Coord(..)

  , Turtle
  , TurtleT

  , TurtleM(..)

  , runTurtle
  , runTurtleT

  , setsLoc
  , setsLoc_

  -- * movement
  , reset
  , moveLeft
  , moveRight
  , moveUp
  , moveDown
  , nextLine

  , wander
 
  ) where


import MonadLib ( MonadT(..) )          -- package: monadLib

import Control.Applicative


data Coord = Coord !Int !Int

instance Show Coord where
  showsPrec i (Coord x y) = showsPrec i (x,y)


newtype Turtle    a = Turtle  { getTurtle  :: Coord -> (a, Coord) } 

newtype TurtleT m a = TurtleT { getTurtleT :: Coord -> m (a, Coord) }


-- Functor

instance Functor Turtle where
  fmap f m = Turtle $ \st -> let (a,st') = getTurtle m st in (f a, st')                   

instance Monad m => Functor (TurtleT m) where
  fmap f m = TurtleT $ \st -> getTurtleT m st >>= \(a,st') ->
                              return (f a, st')

-- Applicative 
instance Applicative Turtle where
  pure a    = Turtle $ \st -> (a,st)
  mf <*> ma = Turtle $ \st -> let (f,st')  = getTurtle mf st 
                                  (a,st'') = getTurtle ma st'
                              in (f a,st'') 


instance Monad m => Applicative (TurtleT m) where
  pure a    = TurtleT $ \st -> return (a,st)
  mf <*> ma = TurtleT $ \st -> getTurtleT mf st  >>= \(f,st')  ->
                               getTurtleT ma st' >>= \(a,st'') ->
                               return (f a,st'') 


-- Monad 

instance Monad Turtle where
  return a = Turtle $ \st -> (a,st)
  m >>= k  = Turtle $ \st -> let (a,st') = getTurtle m st
                             in (getTurtle . k) a st'

instance Monad m => Monad (TurtleT m) where
  return a = TurtleT $ \st -> return (a,st)
  m >>= k  = TurtleT $ \st -> getTurtleT m st        >>= \(a,st')  ->
                              (getTurtleT . k) a st' >>= \(b,st'') ->
                              return (b,st'')

instance MonadT TurtleT where
  lift m = TurtleT $ \st -> m >>= \a -> return (a,st)

class Monad m => TurtleM m where
  getLoc :: m Coord
  setLoc :: Coord -> m ()

instance TurtleM Turtle where
  getLoc   = Turtle $ \st -> (st,st)
  setLoc c = Turtle $ \_  -> ((),c)
  

instance Monad m => TurtleM (TurtleT m) where
  getLoc   = TurtleT $ \st -> return (st,st)
  setLoc c = TurtleT $ \_  -> return ((),c)


runTurtle :: Turtle a -> (a,(Int,Int))
runTurtle mf = post $ getTurtle mf (Coord 0 0) 
  where
    post (a, Coord x y) = (a,(x,y))

runTurtleT :: Monad m => TurtleT m a -> m (a,(Int,Int))
runTurtleT mf = getTurtleT mf (Coord 0 0) >>= \(a, Coord x y) -> return (a,(x,y))


setsLoc :: TurtleM m => (Coord -> (a,Coord)) -> m a
setsLoc f = getLoc >>= \st -> let (a,st') = f st in setLoc st' >> return a

setsLoc_ :: TurtleM m => (Coord -> Coord) -> m ()
setsLoc_ f = getLoc >>= \st -> let st' = f st in setLoc st'

reset       :: TurtleM m => m ()
reset       = setLoc (Coord 0 0)

moveRight   :: TurtleM m => m ()
moveRight   = setsLoc_ $ \(Coord x y) -> Coord (x+1) y

moveLeft    :: TurtleM m => m ()
moveLeft    = setsLoc_ $ \(Coord x y) -> Coord (x-1) y

moveUp      :: TurtleM m => m ()
moveUp      = setsLoc_ $ \(Coord x y) -> Coord x (y-1)

moveDown    :: TurtleM m => m ()
moveDown    = setsLoc_ $ \(Coord x y) -> Coord x (y+1)

nextLine    :: TurtleM m => m ()
nextLine    = setsLoc_ $ \(Coord _ y) -> Coord 0 (y-1)


-- | No longer sure about this combinator...
--
wander :: TurtleM m => m a -> m (a,Coord,Coord)
wander ma = getLoc >>= \start ->
            ma     >>= \ans   ->
            getLoc >>= \end   ->
            return (ans,start,end)