    {-# LANGUAGE GADTs #-}

    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Graphics.Rendering.Chart.Backend.Impl
    -- Copyright   :  (c) Tim Docker 2014
    -- License     :  BSD-style (see chart/COPYRIGHT)
    --
    -- This module provides the implementation details common to all 'ChartBackend's.

    module Graphics.Rendering.Chart.Backend.Impl where

    import Control.Monad
    import Control.Monad.Operational

    import Graphics.Rendering.Chart.Geometry
    import Graphics.Rendering.Chart.Backend.Types

    -- -----------------------------------------------------------------------
    -- Rendering Backend Class
    -- -----------------------------------------------------------------------

    -- | The abstract drawing operation generated when using the
    --   the chart drawing API.
    --
    --   See the documentation of the different function for the correct semantics
    --   of each instruction:
    --
    --   * 'strokePath', 'fillPath'
    --
    --   * 'drawText', 'textSize'
    --
    --   * 'getPointAlignFn', 'getCoordAlignFn', 'AlignmentFns'
    --
    --   * 'withTransform', 'withClipRegion'
    --
    --   * 'withLineStyle', 'withFillStyle', 'withFontStyle'
    --
    data ChartBackendInstr a where
      StrokePath :: Path -> ChartBackendInstr ()
      FillPath   :: Path -> ChartBackendInstr ()
      GetTextSize :: String -> ChartBackendInstr TextSize
      DrawText    :: Point -> String -> ChartBackendInstr ()
      GetAlignments :: ChartBackendInstr AlignmentFns
      WithTransform  :: Matrix ->  Program ChartBackendInstr a -> ChartBackendInstr a
      WithFontStyle  :: FontStyle -> Program ChartBackendInstr a -> ChartBackendInstr a
      WithFillStyle  :: FillStyle -> Program ChartBackendInstr a -> ChartBackendInstr a
      WithLineStyle  :: LineStyle -> Program ChartBackendInstr a -> ChartBackendInstr a
      WithClipRegion :: Rect -> Program ChartBackendInstr a -> ChartBackendInstr a

    -- | A 'BackendProgram' provides the capability to render a chart somewhere.
    --
    --   The coordinate system of the backend has its initial origin (0,0)
    --   in the top left corner of the drawing plane. The x-axis points
    --   towards the top right corner and the y-axis points towards
    --   the bottom left corner. The unit used by coordinates, the font size,
    --   and lengths is the always the same, but depends on the backend.
    --   All angles are measured in radians.
    --
    --   The line, fill and font style are set to their default values
    --   initially.
    --
    --   Information about the semantics of the instructions can be
    --   found in the documentation of 'ChartBackendInstr'.
    type BackendProgram a = Program ChartBackendInstr a

    -- | Stroke the outline of the given path using the
    --   current 'LineStyle'. This function does /not/ perform
    --   alignment operations on the path. See 'Path' for the exact semantic
    --   of paths.
    strokePath :: Path -> BackendProgram ()
    strokePath :: Path -> BackendProgram ()
    strokePath Path
    p = ChartBackendInstr () -> BackendProgram ()
    forall (instr :: * -> *) a (m :: * -> *).
    instr a -> ProgramT instr m a
    singleton (Path -> ChartBackendInstr ()
    StrokePath Path
    p)

    -- | Fill the given path using the current 'FillStyle'.
    --   The given path will be closed prior to filling.
    --   This function does /not/ perform
    --   alignment operations on the path.
    --   See 'Path' for the exact semantic of paths.
    fillPath :: Path -> BackendProgram ()
    fillPath :: Path -> BackendProgram ()
    fillPath Path
    p = ChartBackendInstr () -> BackendProgram ()
    forall (instr :: * -> *) a (m :: * -> *).
    instr a -> ProgramT instr m a
    singleton (Path -> ChartBackendInstr ()
    FillPath Path
    p)

    -- | Calculate a 'TextSize' object with rendering information
    --   about the given string without actually rendering it.
    textSize :: String -> BackendProgram TextSize
    textSize :: String -> BackendProgram TextSize
    textSize String
    text = ChartBackendInstr TextSize -> BackendProgram TextSize
    forall (instr :: * -> *) a (m :: * -> *).
    instr a -> ProgramT instr m a
    singleton (String -> ChartBackendInstr TextSize
    GetTextSize String
    text)

    -- | Draw a single-line textual label anchored by the baseline (vertical)
    --   left (horizontal) point. Uses the current 'FontStyle' for drawing.
    drawText :: Point -> String -> BackendProgram ()
    drawText :: Point -> String -> BackendProgram ()
    drawText Point
    p String
    text = ChartBackendInstr () -> BackendProgram ()
    forall (instr :: * -> *) a (m :: * -> *).
    instr a -> ProgramT instr m a
    singleton (Point -> String -> ChartBackendInstr ()
    DrawText Point
    p String
    text)

    -- | Apply the given transformation in this local
    --   environment when drawing. The given transformation
    --   is applied after the current transformation. This
    --   means both are combined.
    withTransform :: Matrix -> BackendProgram a -> BackendProgram a
    withTransform :: forall a. Matrix -> BackendProgram a -> BackendProgram a
    withTransform Matrix
    t BackendProgram a
    p = ChartBackendInstr a -> BackendProgram a
    forall (instr :: * -> *) a (m :: * -> *).
    instr a -> ProgramT instr m a
    singleton (Matrix -> BackendProgram a -> ChartBackendInstr a
    forall a.
    Matrix -> Program ChartBackendInstr a -> ChartBackendInstr a
    WithTransform Matrix
    t BackendProgram a
    p)

    -- | Use the given font style in this local
    --   environment when drawing text.
    --
    --   An implementing backend is expected to guarantee
    --   to support the following font families: @serif@, @sans-serif@ and @monospace@;
    --
    --   If the backend is not able to find or load a given font
    --   it is required to fall back to a custom fail-safe font
    --   and use it instead.
    withFontStyle :: FontStyle -> BackendProgram a -> BackendProgram a
    withFontStyle :: forall a. FontStyle -> BackendProgram a -> BackendProgram a
    withFontStyle FontStyle
    fs BackendProgram a
    p = ChartBackendInstr a -> BackendProgram a
    forall (instr :: * -> *) a (m :: * -> *).
    instr a -> ProgramT instr m a
    singleton (FontStyle -> BackendProgram a -> ChartBackendInstr a
    forall a.
    FontStyle -> Program ChartBackendInstr a -> ChartBackendInstr a
    WithFontStyle FontStyle
    fs BackendProgram a
    p)

    -- | Use the given fill style in this local
    --   environment when filling paths.
    withFillStyle :: FillStyle -> BackendProgram a -> BackendProgram a
    withFillStyle :: forall a. FillStyle -> BackendProgram a -> BackendProgram a
    withFillStyle FillStyle
    fs BackendProgram a
    p = ChartBackendInstr a -> BackendProgram a
    forall (instr :: * -> *) a (m :: * -> *).
    instr a -> ProgramT instr m a
    singleton (FillStyle -> BackendProgram a -> ChartBackendInstr a
    forall a.
    FillStyle -> Program ChartBackendInstr a -> ChartBackendInstr a
    WithFillStyle FillStyle
    fs BackendProgram a
    p)

    -- | Use the given line style in this local
    --   environment when stroking paths.
    withLineStyle :: LineStyle -> BackendProgram a -> BackendProgram a
    withLineStyle :: forall a. LineStyle -> BackendProgram a -> BackendProgram a
    withLineStyle LineStyle
    ls BackendProgram a
    p = ChartBackendInstr a -> BackendProgram a
    forall (instr :: * -> *) a (m :: * -> *).
    instr a -> ProgramT instr m a
    singleton (LineStyle -> BackendProgram a -> ChartBackendInstr a
    forall a.
    LineStyle -> Program ChartBackendInstr a -> ChartBackendInstr a
    WithLineStyle LineStyle
    ls BackendProgram a
    p)

    -- | Use the given clipping rectangle when drawing
    --   in this local environment. The new clipping region
    --   is intersected with the given clip region. You cannot
    --   escape the clip!
    withClipRegion :: Rect -> BackendProgram a -> BackendProgram a
    withClipRegion :: forall a. Rect -> BackendProgram a -> BackendProgram a
    withClipRegion Rect
    c BackendProgram a
    p = ChartBackendInstr a -> BackendProgram a
    forall (instr :: * -> *) a (m :: * -> *).
    instr a -> ProgramT instr m a
    singleton (Rect -> BackendProgram a -> ChartBackendInstr a
    forall a.
    Rect -> Program ChartBackendInstr a -> ChartBackendInstr a
    WithClipRegion Rect
    c BackendProgram a
    p)

    -- -----------------------------------------------------------------------
    -- Rendering Utility Functions
    -- -----------------------------------------------------------------------

    -- | Get the point alignment function
    getPointAlignFn :: BackendProgram (Point->Point)
    getPointAlignFn :: BackendProgram (Point -> Point)
    getPointAlignFn = (AlignmentFns -> Point -> Point)
    -> ProgramT ChartBackendInstr Identity AlignmentFns
    -> BackendProgram (Point -> Point)
    forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
    liftM AlignmentFns -> Point -> Point
    afPointAlignFn (ChartBackendInstr AlignmentFns
    -> ProgramT ChartBackendInstr Identity AlignmentFns
    forall (instr :: * -> *) a (m :: * -> *).
    instr a -> ProgramT instr m a
    singleton ChartBackendInstr AlignmentFns
    GetAlignments)

    -- | Get the coordinate alignment function
    getCoordAlignFn :: BackendProgram (Point->Point)
    getCoordAlignFn :: BackendProgram (Point -> Point)
    getCoordAlignFn = (AlignmentFns -> Point -> Point)
    -> ProgramT ChartBackendInstr Identity AlignmentFns
    -> BackendProgram (Point -> Point)
    forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
    liftM AlignmentFns -> Point -> Point
    afCoordAlignFn (ChartBackendInstr AlignmentFns
    -> ProgramT ChartBackendInstr Identity AlignmentFns
    forall (instr :: * -> *) a (m :: * -> *).
    instr a -> ProgramT instr m a
    singleton ChartBackendInstr AlignmentFns
    GetAlignments)