{-# LANGUAGE CPP #-}

-- | This module provides a monadic interface to build 'TPath' values.
--   It does so using 'PathBuilder's. The construction of a 'PathBuilder'
--   is equivalent to the construction of a 'TPath' by hand, but with
--   a sometimes more convenient syntax.
--
--   For example, this path corresponds to a triangle:
--
-- > trianglePath :: TPath
-- > trianglePath = bpath (pointAtXY (-1) 0) $ do
-- >    line $ pointAtXY 1 0
-- >    line $ pointAtXY 0 1
-- >    pcycle
--
--   The equivalent syntax created by hand would be:
--
-- > trianglePath :: TPath
-- > trianglePath = Cycle $ Start (pointAtXY (-1) 0) ->- pointAtXY 1 0 ->- pointAtXY 0 1
--
--   The 'Cycle' constructor at the beginning may seem unintuitive, since we are building
--   the path from left to right. In the 'PathBuilder' monad, the instructions are always
--   written in order.
--
module Text.LaTeX.Packages.TikZ.PathBuilder (
   -- * Path builder
   PathBuilder
 , bpath
   -- * Builder functions
 , line
 , pcycle
 , rectangle
 , circle
 , ellipse
 , node
 , grid
   ) where

import Text.LaTeX.Base.Syntax (LaTeX)
import Text.LaTeX.Packages.TikZ.Syntax
import Control.Monad.Trans.State
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative
#endif

data PathState = PS { currentPath :: TPath }

-- | Use a /path builder/ to construct a value of type 'TPath'.
--   Use 'bpath' for this purpose.
data PathBuilder a = PB { pathBuilder :: State PathState a }

-- Instances

instance Functor PathBuilder where
 fmap f (PB st) = PB $ fmap f st

instance Applicative PathBuilder where
 pure = PB . pure
 (PB f) <*> (PB x) = PB $ f <*> x

instance Monad PathBuilder where
 return = pure
 (PB x) >>= f = PB $ x >>= pathBuilder . f

--

applyToPath :: (TPath -> TPath) -> PathBuilder ()
applyToPath f = PB $ modify $ \ps -> ps { currentPath = f (currentPath ps) }

pcycle :: PathBuilder ()
pcycle = applyToPath Cycle

-- | Line from the current point to the given one.
line :: TPoint -> PathBuilder ()
line p = applyToPath $ (`Line`p)

-- | Rectangle with the current point as one cornder and the given point
--   as the opposite corner.
rectangle :: TPoint -> PathBuilder ()
rectangle p = applyToPath $ (`Rectangle`p)

-- | Circle with the given radius centered at the current point.
circle :: Double -> PathBuilder ()
circle r = applyToPath $ (`Circle`r)

-- | Ellipse with width and height described by the arguments and centered
--   at the current point.
ellipse :: Double -- ^ Half width of the ellipse.
        -> Double -- ^ Half height of the ellipse.
        -> PathBuilder ()
ellipse r1 r2 = applyToPath $ \x -> Ellipse x r1 r2

grid :: [GridOption] -> TPoint -> PathBuilder ()
grid xs p = applyToPath $ \x -> Grid x xs p

-- | Text centered at the current point.
node :: LaTeX -> PathBuilder ()
node l = applyToPath $ \x -> Node x l

-- | Build a path using a /starting point/ and a 'PathBuilder'.
bpath :: TPoint -> PathBuilder a -> TPath
bpath p pb = currentPath $ execState (pathBuilder pb) (PS $ Start p)