{-# 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.Reader
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 'ChartBackend' 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 ChartBackend 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 -> ChartBackend ()
strokePath p = singleton (StrokePath 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 -> ChartBackend ()
fillPath p = singleton (FillPath p)

-- | Calculate a 'TextSize' object with rendering information
--   about the given string without actually rendering it.
textSize :: String -> ChartBackend TextSize
textSize text = singleton (GetTextSize text)

-- | Draw a single-line textual label anchored by the baseline (vertical) 
--   left (horizontal) point. Uses the current 'FontStyle' for drawing.
drawText :: Point -> String -> ChartBackend ()
drawText p text = singleton (DrawText p 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 -> ChartBackend a -> ChartBackend a
withTransform t p = singleton (WithTransform t p)

-- | Use the given font style in this local
--   environment when drawing text.
--   
--   An implementing backend is expected to guarentee
--   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 -> ChartBackend a -> ChartBackend a
withFontStyle fs p = singleton (WithFontStyle fs p)

-- | Use the given fill style in this local
--   environment when filling paths.
withFillStyle :: FillStyle -> ChartBackend a -> ChartBackend a
withFillStyle fs p = singleton (WithFillStyle fs p)

-- | Use the given line style in this local
--   environment when stroking paths.
withLineStyle :: LineStyle -> ChartBackend a -> ChartBackend a
withLineStyle ls p = singleton (WithLineStyle ls 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 -> ChartBackend a -> ChartBackend a
withClipRegion c p = singleton (WithClipRegion c p)

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

-- | Get the point alignment function
getPointAlignFn :: ChartBackend (Point->Point)
getPointAlignFn = liftM afPointAlignFn (singleton GetAlignments)

-- | Get the coordinate alignment function
getCoordAlignFn :: ChartBackend (Point->Point)
getCoordAlignFn = liftM afCoordAlignFn (singleton GetAlignments)