{-# LANGUAGE RankNTypes #-}
-- | This module provide lenses compatible with the `lens`
-- module but without the dependency to it.
module Graphics.Rasterific.Lenses
    ( -- * Line lenses
      lineX0
    , lineX1
    , linePoints

      -- * Quadratic bezier curve
    , bezX0
    , bezX1
    , bezX2
    , bezierPoints

      -- * Cubic bezier lenses
    , cbezX0
    , cbezX1
    , cbezX2
    , cbezX3
    , cubicBezierPoints

      -- * Primitive lenses
    , primitivePoints

      -- * Path oriented lenses
    , pathCommandPoints
    , pathPoints

      -- * Type definition to match Lens
    , Lens
    , Lens'
    , Traversal
    , Traversal'
    ) where

import Graphics.Rasterific.Types

-- | Does it look familiar? yes it's the official
-- Lens type.
type Lens s t a b =
    forall f. Functor f => (a -> f b) -> s -> f t

-- | Try to match the Lens' type alias.
type Lens' s a = Lens s s a a

-- | Traversal type, matched to the one of the lens
-- package.
type Traversal s t a b =
    forall f. Applicative f => (a -> f b) -> s -> f t

type Traversal' s a = Traversal s s a a

-- | Create a full lens out of setter and getter
lens :: (s -> a)
     -> (s -> b -> t)
     -> Lens s t a b
{-# INLINE lens #-}
lens accessor setter = \f src ->
  fmap (setter src) $ f (accessor src)

-- | Traverse all the points of a line.
linePoints :: Traversal' Line Point
linePoints f (Line p0 p1) = Line <$> f p0 <*> f p1

-- | Line origin point.
lineX0 :: Lens' Line Point
lineX0 = lens _lineX0 setter where
  setter a b = a { _lineX0 = b }

-- | Line end point.
lineX1 :: Lens' Line Point
lineX1 = lens _lineX1 setter where
  setter a b = a { _lineX1 = b }

-- | Quadratic bezier starting point.
bezX0 :: Lens' Bezier Point
bezX0 = lens _bezierX0 setter where
  setter a b = a { _bezierX0 = b }

-- | bezier control point.
bezX1 :: Lens' Bezier Point
bezX1 = lens _bezierX1 setter where
  setter a b = a { _bezierX1 = b }

-- | bezier end point.
bezX2 :: Lens' Bezier Point
bezX2 = lens _bezierX2 setter where
  setter a b = a { _bezierX2 = b }

-- | Traversal of all the bezier's points.
bezierPoints :: Traversal' Bezier Point
bezierPoints f (Bezier p0 p1 p2) =
  Bezier <$> f p0 <*> f p1 <*> f p2

-- | Cubic bezier first point
cbezX0 :: Lens' CubicBezier Point
cbezX0 = lens _cBezierX0 setter where
  setter a b = a { _cBezierX0 = b }

-- | Cubic bezier first control point.
cbezX1 :: Lens' CubicBezier Point
cbezX1 = lens _cBezierX1 setter where
  setter a b = a { _cBezierX1 = b }

-- | Cubic bezier second control point.
cbezX2 :: Lens' CubicBezier Point
cbezX2 = lens _cBezierX2 setter where
  setter a b = a { _cBezierX2 = b }

-- | Cubic bezier last point.
cbezX3 :: Lens' CubicBezier Point
cbezX3 = lens _cBezierX2 setter where
  setter a b = a { _cBezierX3 = b }

-- | Traversal of all the points of the cubic bezier.
cubicBezierPoints :: Traversal' CubicBezier Point
cubicBezierPoints f (CubicBezier p0 p1 p2 p3) =
  CubicBezier <$> f p0 <*> f p1 <*> f p2 <*> f p3

-- | Traverse all the points defined in the primitive.
primitivePoints :: Traversal' Primitive Point
primitivePoints f (LinePrim l) = LinePrim <$> linePoints f l
primitivePoints f (BezierPrim b) = BezierPrim <$> bezierPoints f b
primitivePoints f (CubicBezierPrim c) =
    CubicBezierPrim <$> cubicBezierPoints f c

-- | Traversal of all the points of a path
pathCommandPoints :: Traversal' PathCommand Point
pathCommandPoints f (PathLineTo p) = PathLineTo <$> f p
pathCommandPoints f (PathQuadraticBezierCurveTo p1 p2) =
    PathQuadraticBezierCurveTo <$> f p1 <*> f p2
pathCommandPoints f (PathCubicBezierCurveTo p1 p2 p3) =
    PathCubicBezierCurveTo <$> f p1 <*> f p2 <*> f p3

-- | Traversal of all the points in a path.
pathPoints :: Traversal' Path Point
pathPoints f (Path p0 yn comms) =
  Path <$> f p0 <*> pure yn <*> traverse (pathCommandPoints f) comms