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

--------------------------------------------------------------------------------
-- |
-- Module      :  Wumpus.Basic.Monads.Drawing
-- Copyright   :  (c) Stephen Tetley 2010
-- License     :  BSD3
--
-- Maintainer  :  stephen.tetley@gmail.com
-- Stability   :  unstable
-- Portability :  GHC 
--
-- Graphic types and lifters...
--
--
--
--------------------------------------------------------------------------------

module Wumpus.Basic.Monads.Drawing
  (
    
    AGraphic(..)
  , ANode
  , AFreeGraphic
  , AConnector

  , node
  , nodeAt
  , at
  , liftAFG
  , connect
  , connect_
  , props

  -- doodle
  , thick

  ) where

import Wumpus.Basic.Graphic
import Wumpus.Basic.Graphic.DrawingAttr
import Wumpus.Basic.Monads.DrawingCtxClass
import Wumpus.Basic.Monads.TraceClass
import Wumpus.Basic.Monads.TurtleClass

import Wumpus.Core                              -- package: wumpus-core

import Control.Applicative

-- | AGraphic 
-- 
-- param typically @Point2 u@ or @()@
--
-- If the param is a point it will be supplied by the drawing 
-- mode / drawing monad (e.g. the Turtle monad which supplies
-- the current point).
--
data AGraphic param u a = AGraphic 
       { agDrawF    :: DrawingAttr -> param -> Graphic u
       , agMakeF    :: DrawingAttr -> param -> a
       }


-- | ANode is drawn a a point supplied by the drawing 
-- (e.g. current node of Turtle).
--
type ANode u a = AGraphic (Point2 u) u a

-- /Free/ graphic
--
type AFreeGraphic u a = AGraphic () u a

type AConnector u a = Point2 u -> Point2 u -> AFreeGraphic u a

instance Functor (AGraphic pm u) where
  fmap f (AGraphic df mf) = AGraphic df (\pt attr -> f $ mf pt attr)

instance Applicative (AGraphic pm u) where
  pure a = AGraphic (\_ _ -> id) (\_ _ -> a)
  (AGraphic df1 mf1) <*> (AGraphic df2 mf2) = AGraphic df mf
      where
        df attr pt   = df2 attr pt . df1 attr pt
        mf attr pt   = mf1 attr pt $ mf2 attr pt


-- This doesn't work like at on MGraphicF, as the point is not 
-- scaled w.r.t. TurtleScaleM ...
--
at :: ANode u a -> Point2 u -> ANode u a
at (AGraphic df mf) pt = AGraphic (\attr _ -> df attr pt)
                                  (\attr _ -> mf attr pt)



-- getPos should be a class method outside of Turtle
-- those Bivariate context from PSC could implement it...

node :: (Num u, TraceM m u, DrawingCtxM m, TurtleScaleM m u) 
     => ANode u a -> m a
node (AGraphic df mf) = 
    askDrawingCtx >>= \attr ->
    getPos        >>= \pt   ->
    trace (df attr pt) >> return (mf attr pt)

nodeAt :: (Num u, TraceM m u, DrawingCtxM m) 
       => ANode u a -> Point2 u -> m a
nodeAt (AGraphic df mf) pt = 
    askDrawingCtx >>= \attr ->
    trace (df attr pt) >> return (mf attr pt)


liftAFG :: (Num u, TraceM m u, DrawingCtxM m) 
        => AFreeGraphic u a -> m a
liftAFG (AGraphic df mf) = 
    askDrawingCtx >>= \attr -> trace (df attr ()) >> return (mf attr ())



connect :: (Num u, TraceM m u, DrawingCtxM m) 
        => AConnector u a -> Point2 u -> Point2 u -> m a
connect conn p1 p2 = let (AGraphic df mf) = conn p1 p2 in  
    askDrawingCtx >>= \attr -> trace (df attr ()) >> return (mf attr ())


-- This is a bit unfortunate - with a connector we can\'t touch
-- the drawingAttr inside the AGraphic becase a connecter  is
--
-- > pt -> pt -> AGraphic
--
-- and not
--
-- > AGraphic
--
--
-- Maybe AGraphic shouldn\'t have the agAttrF field?
--
--
--
connect_ :: (Num u, TraceM m u, DrawingCtxM m) 
         => (DrawingAttr -> DrawingAttr) 
         -> AConnector u a -> Point2 u -> Point2 u -> m a
connect_ fn conn p1 p2 = let (AGraphic df mf) = conn p1 p2 in  
    askDrawingCtx >>= \a0 ->
    let attr = fn $ a0 in trace (df attr ()) >> return (mf attr ())




infixr 7 `props`

props :: AGraphic pm u a -> (DrawingAttr -> DrawingAttr) -> AGraphic pm u a
props (AGraphic df mf) upd = AGraphic (\attr p -> df (upd attr) p) 
                                      (\attr p -> mf (upd attr) p)