{-# LANGUAGE TypeFamilies               #-}
{-# OPTIONS -Wall #-}

--------------------------------------------------------------------------------
-- |
-- Module      :  Wumpus.Basic.Kernel.Objects.Connector
-- Copyright   :  (c) Stephen Tetley 2011
-- License     :  BSD3
--
-- Maintainer  :  stephen.tetley@gmail.com
-- Stability   :  highly unstable
-- Portability :  GHC 
--
-- ConnImage and ConnGraphic types - these are functional types
-- from the DrawingContext plus start point and end point to a 
-- graphic /primitive/.
--
--------------------------------------------------------------------------------

module Wumpus.Basic.Kernel.Objects.Connector
   (
     ConnectorImage
   , ConnectorGraphic

   , DConnectorImage
   , DConnectorGraphic

   , ConnectorQuery

   , runConnectorImage
   , runConnectorQuery
   , connect

   , stripConnectorImage
   , liftConnectorQuery

   , promoteConn
   , applyConn

   , qpromoteConn
   , qapplyConn

   , emptyConnectorImage

   )

   where

import Wumpus.Basic.Kernel.Base.BaseDefs
import Wumpus.Basic.Kernel.Base.DrawingContext
import Wumpus.Basic.Kernel.Base.QueryDC
import Wumpus.Basic.Kernel.Objects.Basis
import Wumpus.Basic.Kernel.Objects.Image

import Wumpus.Core                              -- package: wumpus-core

import Control.Applicative
import Data.Monoid


-- | ConnectorImage - function from DrawingContext and start and 
-- end points to a polymorphic /answer/ and a graphic /primitive/.
--
newtype ConnectorImage u a = ConnectorImage { 
          getConnectorImage :: DPoint2 -> DPoint2 -> Image u a }


type instance DUnit (ConnectorImage u a) = u



type ConnectorGraphic u = ConnectorImage u (UNil u)

-- | Type specialized version of 'ConnectorImage'.
--
type DConnectorImage a        = ConnectorImage Double a

-- | Type specialized version of 'ConnectorGraphic'.
--
type DConnectorGraphic        = ConnectorGraphic Double 


newtype ConnectorQuery u a = ConnectorQuery { 
          getConnectorQuery :: DPoint2 -> DPoint2 -> Query u a }


-- Functor 

instance Functor (ConnectorImage u) where
  fmap f ma = ConnectorImage $ \p0 p1 -> fmap f $ getConnectorImage ma p0 p1

instance Functor (ConnectorQuery u) where
  fmap f ma = ConnectorQuery $ \p0 p1 -> fmap f $ getConnectorQuery ma p0 p1


-- Applicative

instance Applicative (ConnectorImage u) where
  pure a    = ConnectorImage $ \_  _  -> pure a
  mf <*> ma = ConnectorImage $ \p0 p1 -> 
                getConnectorImage mf p0 p1 <*> getConnectorImage ma p0 p1

instance Applicative (ConnectorQuery u) where
  pure a    = ConnectorQuery $ \_  _  -> pure a
  mf <*> ma = ConnectorQuery $ \p0 p1 -> 
                getConnectorQuery mf p0 p1 <*> getConnectorQuery ma p0 p1


-- Monad 

instance Monad (ConnectorImage u) where
  return a  = ConnectorImage $ \_  _  -> return a
  ma >>= k  = ConnectorImage $ \p0 p1 -> 
                getConnectorImage ma p0 p1 >>= \ans -> 
                getConnectorImage (k ans) p0 p1


instance Monad (ConnectorQuery u) where
  return a  = ConnectorQuery $ \_  _  -> return a
  ma >>= k  = ConnectorQuery $ \p0 p1 -> 
                getConnectorQuery ma p0 p1 >>= \ans -> 
                getConnectorQuery (k ans) p0 p1


-- Monoid

instance Monoid a => Monoid (ConnectorImage u a) where
  mempty          = pure mempty
  ma `mappend` mb = ConnectorImage $ \p0 p1 -> 
                      getConnectorImage ma p0 p1 
                        `mappend` getConnectorImage mb p0 p1 


instance Monoid a => Monoid (ConnectorQuery u a) where
  mempty          = pure mempty
  ma `mappend` mb = ConnectorQuery $ \p0 p1 -> 
                      getConnectorQuery ma p0 p1 
                        `mappend` getConnectorQuery mb p0 p1 



-- DrawingCtxM

instance DrawingCtxM (ConnectorImage u) where
  askDC           = ConnectorImage $ \_  _  -> askDC
  asksDC fn       = ConnectorImage $ \_  _  -> asksDC fn
  localize upd ma = ConnectorImage $ \p0 p1 -> 
                      localize upd (getConnectorImage ma p0 p1)

instance DrawingCtxM (ConnectorQuery u) where
  askDC           = ConnectorQuery $ \_  _  -> askDC
  asksDC fn       = ConnectorQuery $ \_  _  -> asksDC fn
  localize upd ma = ConnectorQuery $ \p0 p1 -> 
                      localize upd (getConnectorQuery ma p0 p1)


  


instance Decorate ConnectorImage where
  decorate zo ma mz = ConnectorImage $ \p0 p1 -> 
    decorate zo (getConnectorImage ma p0 p1) (getConnectorImage mz p0 p1)

  elaborate zo ma f = ConnectorImage $ \p0 p1 -> 
    elaborate zo (getConnectorImage ma p0 p1) 
                 (\a -> getConnectorImage (f a) p0 p1)

  obliterate ma = ConnectorImage $ \p0 p1 -> 
    obliterate $ getConnectorImage ma p0 p1

  hyperlink xl ma = ConnectorImage $ \p0 p1 -> 
    hyperlink xl $ getConnectorImage ma p0 p1



runConnectorImage :: InterpretUnit u 
                  => ConnectorImage u a -> DrawingContext 
                  -> Point2 u -> Point2 u
                  -> PrimResult u a
runConnectorImage ma ctx p0 p1 = 
    let dp0 = normalizeF (dc_font_size ctx) p0
        dp1 = normalizeF (dc_font_size ctx) p1 
    in runImage (getConnectorImage ma dp0 dp1) ctx


runConnectorQuery :: InterpretUnit u 
                  => ConnectorQuery u a -> DrawingContext 
                  -> Point2 u -> Point2 u 
                  -> a
runConnectorQuery ma ctx p0 p1 = 
    let dp0 = normalizeF (dc_font_size ctx) p0
        dp1 = normalizeF (dc_font_size ctx) p1 
    in runQuery (getConnectorQuery ma dp0 dp1) ctx


connect :: InterpretUnit u 
        => Point2 u -> Point2 u -> ConnectorImage u a -> Image u a
connect p0 p1 ma = normalizeCtxF p0 >>= \dp0 -> 
                   normalizeCtxF p1 >>= \dp1 -> 
                   getConnectorImage ma dp0 dp1


stripConnectorImage :: ConnectorImage u a -> ConnectorQuery u a
stripConnectorImage ma = ConnectorQuery $ \p1 p2 -> 
    stripImage $ getConnectorImage ma p1 p2


liftConnectorQuery :: ConnectorQuery u a -> ConnectorImage u a
liftConnectorQuery ma = ConnectorImage $ \p1 p2 -> 
    liftQuery $ getConnectorQuery ma p1 p2


promoteConn :: InterpretUnit u 
            => (Point2 u -> Point2 u -> Image u a) -> ConnectorImage u a
promoteConn k = ConnectorImage $ \p0 p1 ->
    dinterpCtxF p0 >>= \up0 -> 
    dinterpCtxF p1 >>= \up1 -> 
    k up0 up1

applyConn :: InterpretUnit u 
          => ConnectorImage u a -> Point2 u -> Point2 u -> Image u a
applyConn ma p0 p1 = normalizeCtxF p0 >>= \dp0 -> 
                     normalizeCtxF p1 >>= \dp1 -> 
                     getConnectorImage ma dp0 dp1




qpromoteConn :: InterpretUnit u 
             => (Point2 u -> Point2 u -> Query u a) -> ConnectorQuery u a
qpromoteConn k = ConnectorQuery $ \p0 p1 ->
    dinterpCtxF p0 >>= \up0 -> 
    dinterpCtxF p1 >>= \up1 -> 
    k up0 up1

qapplyConn :: InterpretUnit u
           => ConnectorQuery u a -> Point2 u -> Point2 u -> Query u a
qapplyConn ma p0 p1 = normalizeCtxF p0 >>= \dp0 -> 
                      normalizeCtxF p1 >>= \dp1 -> 
                      getConnectorQuery ma dp0 dp1


--------------------------------------------------------------------------------
-- UConvert instance

instance UConvert ConnectorImage where
  uconvF = uconvConnectorImageF
  uconvZ = uconvConnectorImageZ


-- | Use this to convert 'ConnectorGraphic' or 'ConnectorImage' 
-- with Functor answer.
--
uconvConnectorImageF :: (InterpretUnit u, InterpretUnit u1, Functor t) 
                     => ConnectorImage u (t u) -> ConnectorImage u1 (t u1)
uconvConnectorImageF ma = ConnectorImage $ \p0 p1 -> 
    uconvF $ getConnectorImage ma p0 p1




-- | Use this to convert 'ConnectorImage' with unit-less answer.
--
uconvConnectorImageZ :: (InterpretUnit u, InterpretUnit u1) 
                     => ConnectorImage u a -> ConnectorImage u1 a
uconvConnectorImageZ ma = ConnectorImage $ \p0 p1 -> 
    uconvZ $ getConnectorImage ma p0 p1

-- | Having /empty/ at the specific 'ConnectorImage' type is useful.
-- 
emptyConnectorImage :: Monoid a => ConnectorImage u a
emptyConnectorImage = mempty


--------------------------------------------------------------------------------



--
-- Design note - potentially there are no useful combining 
-- operators on Connectors (!).
--
-- Division - i.e. splitting a path at points between the start 
-- and end - seems a more obvious operation on connector paths 
-- than combination. See the ConnectorPath operations in 
-- Wumpus-Drawing for some examples.
--