content: {-# LANGUAGE FlexibleInstances          #-}
  {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  {-# LANGUAGE MultiParamTypeClasses      #-}
  {-# LANGUAGE TypeFamilies               #-}
  {-# OPTIONS_GHC -fno-warn-unused-imports       #-}
  -----------------------------------------------------------------------------
  -- |
  -- Module      :  Diagrams.Core.Query
  -- Copyright   :  (c) 2011-2015 diagrams-core team (see LICENSE)
  -- License     :  BSD-style (see LICENSE)
  -- Maintainer  :  diagrams-discuss@googlegroups.com
  --
  -- The @Query@ module defines a type for \"queries\" on diagrams, which
  -- are functions from points in a vector space to some monoid.
  --
  -----------------------------------------------------------------------------
  
  module Diagrams.Core.Query
    ( Query (..)
    ) where
  
  import           Control.Applicative
  import           Control.Lens
  import           Data.Semigroup
  import           Data.Distributive
  import           Data.Functor.Rep
  import           Data.Profunctor
  import           Data.Profunctor.Sieve
  import           Data.Profunctor.Closed
  import qualified Data.Profunctor.Rep    as P
  
  import           Linear.Affine
  import           Linear.Vector
  
  import           Diagrams.Core.HasOrigin
  import           Diagrams.Core.Transform
  import           Diagrams.Core.V
  
  ------------------------------------------------------------------------
  -- Queries
  ------------------------------------------------------------------------
  
  -- | A query is a function that maps points in a vector space to
  --   values in some monoid. Queries naturally form a monoid, with
  --   two queries being combined pointwise.
  --
  --   The idea for annotating diagrams with monoidal queries came from
  --   the graphics-drawingcombinators package,
  --   <http://hackage.haskell.org/package/graphics-drawingcombinators>.
  newtype Query v n m = Query { runQuery :: Point v n -> m }
    deriving (Functor, Applicative, Monad, Semigroup, Monoid)
  
  instance Distributive (Query v n) where
    distribute a = Query $ \p -> fmap (\(Query q) -> q p) a
  
  instance Representable (Query v n) where
    type Rep (Query v n) = Point v n
    tabulate = Query
    index    = runQuery
  
  instance Functor v => Profunctor (Query v) where
    lmap f (Query q) = Query $ \p -> q (fmap f p)
    rmap = fmap
  
  instance Functor v => Cosieve (Query v) (Point v) where
    cosieve = runQuery
  
  instance Functor v => Closed (Query v) where
    closed (Query fab) = Query $ \fxa x -> fab (fmap ($ x) fxa)
  
  instance Functor v => Costrong (Query v) where
    unfirst (Query f) = Query f'
      where f' fa = b where (b, d) = f ((\a -> (a, d)) <$> fa)
    unsecond (Query f) = Query f'
      where f' fa = b where (d, b) = f ((,) d <$> fa)
  
  instance Functor v => P.Corepresentable (Query v) where
    type Corep (Query v) = Point v
    cotabulate = Query
  
  -- | Setter over the input point of a query.
  queryPoint :: Setter (Query v' n' m) (Query v n m) (Point v n) (Point v' n')
  queryPoint = sets $ \f (Query q) -> Query $ q . f
  
  instance Wrapped (Query v n m) where
    type Unwrapped (Query v n m) = Point v n -> m
    _Wrapped' = iso runQuery Query
  
  instance Rewrapped (Query v a m) (Query v' a' m')
  
  type instance V (Query v n m) = v
  type instance N (Query v n m) = n
  
  instance (Additive v, Num n) => HasOrigin (Query v n m) where
    moveOriginTo (P u) = queryPoint %~ (.+^ u)
  
  instance (Additive v, Num n) => Transformable (Query v n m) where
    transform t = queryPoint %~ papply (inv t)
  
  