    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Graphics.Rendering.Chart.Layout
    -- Copyright   :  (c) Tim Docker 2006, 2014
    -- License     :  BSD-style (see chart/COPYRIGHT)
    --
    -- This module glues together axes and plots to actually create a renderable
    -- for a chart.
    --
    -- Note that Template haskell is used to derive accessor functions
    -- (see 'Control.Lens') for each field of the following data types:
    --
    --     * 'Layout'
    --
    --     * 'LayoutLR'
    --
    --     * 'StackedLayouts'
    --
    --     * 'LayoutAxis'
    --
    {-# LANGUAGE ScopedTypeVariables #-}
    {-# LANGUAGE TemplateHaskell #-}
    {-# LANGUAGE ExistentialQuantification #-}

    module Graphics.Rendering.Chart.Layout
      ( -- * Types
        Layout(..)
      , LayoutLR(..)
      , LayoutAxis(..)
      , LayoutPick(..)
      , StackedLayouts(..)
      , StackedLayout(..)
      -- , LegendItem  haddock complains about this being missing, but from what?
      , MAxisFn

        -- * Rendering
      , layoutToRenderable
      , layoutToGrid
      , layoutLRToRenderable
      , layoutLRToGrid
      , renderStackedLayouts

        -- * LayoutAxis lenses
      , laxis_title_style
      , laxis_title
      , laxis_style
      , laxis_generate
      , laxis_override
      , laxis_reverse

        -- * Layout lenses
      , layout_background
      , layout_plot_background
      , layout_title
      , layout_title_style
      , layout_x_axis
      , layout_top_axis_visibility
      , layout_bottom_axis_visibility
      , layout_y_axis
      , layout_left_axis_visibility
      , layout_right_axis_visibility
      , layout_margin
      , layout_plots
      , layout_legend
      , layout_grid_last

      , layout_axes_styles
      , layout_axes_title_styles
      , layout_all_font_styles
      , layout_foreground

        -- * LayoutLR lenses
      , layoutlr_background
      , layoutlr_plot_background
      , layoutlr_title
      , layoutlr_title_style
      , layoutlr_x_axis
      , layoutlr_top_axis_visibility
      , layoutlr_bottom_axis_visibility
      , layoutlr_left_axis
      , layoutlr_right_axis
      , layoutlr_left_axis_visibility
      , layoutlr_right_axis_visibility
      , layoutlr_plots
      , layoutlr_legend
      , layoutlr_margin
      , layoutlr_grid_last

      , layoutlr_axes_styles
      , layoutlr_axes_title_styles
      , layoutlr_all_font_styles
      , layoutlr_foreground

        -- * StackedLayouts lenses
      , slayouts_layouts
      , slayouts_compress_legend
      ) where

    import Graphics.Rendering.Chart.Axis
    import Graphics.Rendering.Chart.Geometry
    import Graphics.Rendering.Chart.Drawing
    import Graphics.Rendering.Chart.Utils
    import Graphics.Rendering.Chart.Plot
    import Graphics.Rendering.Chart.Legend
    import Graphics.Rendering.Chart.Renderable
    import Graphics.Rendering.Chart.Grid
    import Control.Monad
    import Control.Lens hiding (at)
    import Data.Colour
    import Data.Colour.Names (white)
    import Data.Default.Class

    -- | A @MAxisFn@ is a function that generates an (optional) axis
    --   given the points plotted against that axis.
    type MAxisFn t = [t] -> Maybe (AxisData t)

    -- | Type of axis that is used in 'Layout' and 'LayoutLR'.
    --
    --   To generate the actual axis type ('AxisData' and 'AxisT')
    --   the '_laxis_generate' function is called and custom settings
    --   are applied with '_laxis_override'. Note that the 'AxisVisibility'
    --   values in 'Layout' and 'LayoutLR' override visibility related
    --   settings of the axis.
    data LayoutAxis x = LayoutAxis
      { _laxis_title_style :: FontStyle
        -- ^ Font style to use for the axis title.
      , _laxis_title       :: String
        -- ^ Title displayed for the axis.
      , _laxis_style       :: AxisStyle
        -- ^ Axis style applied.

      , _laxis_generate    :: AxisFn x
        -- ^ Function that generates the axis data, based upon the
        --   points plotted. The default value is 'autoAxis'.

      , _laxis_override    :: AxisData x -> AxisData x
        -- ^ Function that can be used to override the generated axis data.
        --   The default value is 'id'.

      , _laxis_reverse     :: Bool
        -- ^ True if left to right (bottom to top) is to show descending values.

      }

    -- | Information on what is at a specifc location of a 'Layout' or 'LayoutLR'.
    --   This is delivered by the 'PickFn' of a 'Renderable'.
    data LayoutPick x y1 y2 = LayoutPick_Legend String -- ^ A legend entry.
                              | LayoutPick_Title String  -- ^ The title.
                              | LayoutPick_XTopAxisTitle String      -- ^ The title of the top x axis.
                              | LayoutPick_XBottomAxisTitle String   -- ^ The title of the bottom x axis.
                              | LayoutPick_YLeftAxisTitle String  -- ^ The title of the left y axis.
                              | LayoutPick_YRightAxisTitle String -- ^ The title of the right y axis.
                              | LayoutPick_PlotArea x y1 y2 -- ^ The plot area at the given plot coordinates.
                              | LayoutPick_XTopAxis x       -- ^ The top x axis at the given plot coordinate.
                              | LayoutPick_XBottomAxis x    -- ^ The bottom x axis at the given plot coordinate.
                              | LayoutPick_YLeftAxis y1  -- ^ The left y axis at the given plot coordinate.
                              | LayoutPick_YRightAxis y2 -- ^ The right y axis at the given plot coordinate.
                              deriving (Show)

    type LegendItem = (String,Rect -> BackendProgram ())

    -- | A Layout value is a single plot area, with single x and y
    --   axis. The title is at the top and the legend at the bottom. It's
    --   parametrized by the types of values to be plotted on the x
    --   and y axes.
    data Layout x y = Layout
      { _layout_background      :: FillStyle
        -- ^ How to fill the background of everything.
      , _layout_plot_background :: Maybe FillStyle
        -- ^ How to fill the background of the plot,
        --   if different from the overall background.

      , _layout_title           :: String
        -- ^ Title to display above the chart.
      , _layout_title_style     :: FontStyle
        -- ^ Font style to use for the title.

      , _layout_x_axis                 :: LayoutAxis x
        -- ^ Rules to generate the x axis.
      , _layout_top_axis_visibility    :: AxisVisibility
        -- ^ Visibility options for the top axis.
      , _layout_bottom_axis_visibility :: AxisVisibility
        -- ^ Visibility options for the bottom axis.

      , _layout_y_axis                :: LayoutAxis y
        -- ^ Rules to generate the y axis.
      , _layout_left_axis_visibility  :: AxisVisibility
        -- ^ Visibility options for the left axis.
      , _layout_right_axis_visibility :: AxisVisibility
        -- ^ Visibility options for the right axis.

      , _layout_plots           :: [Plot x y]
        -- ^ The data sets to plot in the chart.
        --   The are ploted over each other.

      , _layout_legend          :: Maybe LegendStyle
        -- ^ How to style the legend.
      , _layout_margin          :: Double
        -- ^ The margin distance to use.
      , _layout_grid_last       :: Bool
        -- ^ If the grid shall be rendered
        --   beneath (@False@) or over (@True@) all plots.
      }

    instance (Ord x, Ord y) => ToRenderable (Layout x y) where
      toRenderable = setPickFn nullPickFn . layoutToRenderable

    -- | Render the given 'Layout'.
    layoutToRenderable :: forall x y . (Ord x, Ord y) => Layout x y -> Renderable (LayoutPick x y y)
    layoutToRenderable l = fillBackground (_layout_background l) $ gridToRenderable (layoutToGrid l)

    layoutToGrid :: forall x y . (Ord x, Ord y) => Layout x y -> Grid (Renderable (LayoutPick x y y))
    layoutToGrid l = grid
      where
        grid = titleToRenderable lm (_layout_title_style l) (_layout_title l)
               `wideAbove`
               addMarginsToGrid (lm,lm,lm,lm) (layoutPlotAreaToGrid l)
               `aboveWide`
               renderLegend l (getLegendItems l)
        lm = _layout_margin l

    getLayoutXVals :: Layout x y -> [x]
    getLayoutXVals l = concatMap (fst . _plot_all_points) (_layout_plots l)

    -- | Extract all 'LegendItem's from the plots of a 'Layout'.
    getLegendItems :: Layout x y -> [LegendItem]
    getLegendItems l = concat [ _plot_legend p | p <- _layout_plots l ]

    -- | Render the given 'LegendItem's for a 'Layout'.
    renderLegend :: Layout x y -> [LegendItem] -> Renderable (LayoutPick x y y)
    renderLegend l legItems = gridToRenderable g
      where
        g      = besideN [ tval $ mkLegend (_layout_legend l) (_layout_margin l) legItems
                         , weights (1,1) $ tval emptyRenderable ]

    -- | Render the plot area of a 'Layout'. This consists of the
    --   actual plot area with all plots, the axis and their titles.
    layoutPlotAreaToGrid :: forall x y. (Ord x, Ord y) =>
                            Layout x y -> Grid (Renderable (LayoutPick x y y))
    layoutPlotAreaToGrid l = buildGrid LayoutGridElements{
      lge_plots = mfill (_layout_plot_background l) $ plotsToRenderable l,
      lge_taxis = (tAxis,_laxis_title $ _layout_x_axis l, _laxis_title_style $ _layout_x_axis l),
      lge_baxis = (bAxis,_laxis_title $ _layout_x_axis l, _laxis_title_style $ _layout_x_axis l),
      lge_laxis = (lAxis,_laxis_title $ _layout_y_axis l, _laxis_title_style $ _layout_y_axis l),
      lge_raxis = (rAxis,"", def),
      lge_margin = _layout_margin l
      }
      where
        xvals = [ x | p <- _layout_plots l, x <- fst $ _plot_all_points p]
        yvals = [ y | p <- _layout_plots l, y <- snd $ _plot_all_points p]

        bAxis = mkAxis E_Bottom (overrideAxisVisibility l _layout_x_axis _layout_bottom_axis_visibility) xvals
        tAxis = mkAxis E_Top    (overrideAxisVisibility l _layout_x_axis _layout_top_axis_visibility   ) xvals
        lAxis = mkAxis E_Left   (overrideAxisVisibility l _layout_y_axis _layout_left_axis_visibility  ) yvals
        rAxis = mkAxis E_Right  (overrideAxisVisibility l _layout_y_axis _layout_right_axis_visibility ) yvals
        axes = (bAxis,lAxis,tAxis,rAxis)

        plotsToRenderable lxy = Renderable {
            minsize = return (0,0),
            render  = renderPlots lxy
        }

        -- | Render the plots of a 'Layout' to a plot area of given size.
        renderPlots :: Layout x y -> RectSize -> BackendProgram (PickFn (LayoutPick x y y))
        renderPlots lxy sz@(w,h) = do
            unless (_layout_grid_last lxy) (renderGrids sz axes)
            withClipRegion (Rect (Point 0 0) (Point w h)) $
              mapM_ rPlot (_layout_plots lxy)
            when (_layout_grid_last lxy) (renderGrids sz axes)
            return pickfn
          where
            rPlot = renderSinglePlot sz bAxis lAxis

            xr = (0, w)
            yr = (h, 0)

            pickfn :: PickFn (LayoutPick x y y)
            pickfn (Point x y) = do  -- Maybe monad
                xat <- mxat
                yat <- myat
                return (LayoutPick_PlotArea (mapx xat x) (mapy yat y) (mapy yat y))
              where
                mxat = case (bAxis,tAxis) of
                    (Just at,_)       -> Just at
                    (_,Just at)       -> Just at
                    (Nothing,Nothing) -> Nothing
                myat = case (lAxis,rAxis) of
                    (Just at,_)   -> Just at
                    (_,Just at)   -> Just at
                    (Nothing,Nothing)   -> Nothing
                mapx (AxisT _ _ rev ad) = _axis_tropweiv ad (optPairReverse rev xr)
                mapy (AxisT _ _ rev ad) = _axis_tropweiv ad (optPairReverse rev yr)

    -- | Empty 'Layout' without title and plots. The background is white and
    --   the grid is drawn beneath all plots. There will be a legend. The top
    --   and right axis will not be visible.
    instance (PlotValue x, PlotValue y) => Default (Layout x y) where
      def = Layout
        { _layout_background      = solidFillStyle $ opaque white
        , _layout_plot_background = Nothing

        , _layout_title           = ""
        , _layout_title_style     = def { _font_size   = 15
                                        , _font_weight = FontWeightBold }

        , _layout_x_axis                 = def
        , _layout_top_axis_visibility    = def { _axis_show_line   = False
                                               , _axis_show_ticks  = False
                                               , _axis_show_labels = False }
        , _layout_bottom_axis_visibility = def
        , _layout_y_axis                 = def
        , _layout_left_axis_visibility   = def
        , _layout_right_axis_visibility  = def { _axis_show_line   = False
                                               , _axis_show_ticks  = False
                                               , _axis_show_labels = False }

        , _layout_margin          = 10
        , _layout_plots           = []
        , _layout_legend          = Just def
        , _layout_grid_last       = False
        }

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

    -- | A LayoutLR value is a single plot area, with an x axis and
    --   independent left and right y axes, with a title at the top;
    --   legend at the bottom. It's parametrized by the types of values
    --   to be plotted on the x and two y axes.
    data LayoutLR x y1 y2 = LayoutLR
      { _layoutlr_background      :: FillStyle
        -- ^ How to fill the background of everything.
      , _layoutlr_plot_background :: Maybe FillStyle
        -- ^ How to fill the background of the plot,
        --   if different from the overall background.

      , _layoutlr_title           :: String
        -- ^ Title to display above the chart.
      , _layoutlr_title_style     :: FontStyle
        -- ^ Font style to use for the title.

      , _layoutlr_x_axis                 :: LayoutAxis x
        -- ^ Rules to generate the x axis.
      , _layoutlr_top_axis_visibility    :: AxisVisibility
        -- ^ Visibility options for the top axis.
      , _layoutlr_bottom_axis_visibility :: AxisVisibility
        -- ^ Visibility options for the bottom axis.

      , _layoutlr_left_axis             :: LayoutAxis y1
        -- ^ Rules to generate the left y axis.
      , _layoutlr_left_axis_visibility  :: AxisVisibility
        -- ^ Visibility options for the left axis.
      , _layoutlr_right_axis            :: LayoutAxis y2
        -- ^ Rules to generate the right y axis.
      , _layoutlr_right_axis_visibility :: AxisVisibility
        -- ^ Visibility options for the right axis.

      , _layoutlr_plots      :: [Either (Plot x y1) (Plot x y2)]
        -- ^ The data sets to plot in the chart.
        --   The are ploted over each other.
        --   The either type associates the plot with the
        --   left or right y axis.

      , _layoutlr_legend          :: Maybe LegendStyle
        -- ^ How to style the legend.
      , _layoutlr_margin          :: Double
        -- ^ The margin distance to use.
      , _layoutlr_grid_last       :: Bool
        -- ^ If the grid shall be rendered
        --   beneath (@False@) or over (@True@) all plots.
      }

    instance (Ord x, Ord yl, Ord yr) => ToRenderable (LayoutLR x yl yr) where
      toRenderable = setPickFn nullPickFn . layoutLRToRenderable

    -- | Render the given 'LayoutLR'.
    layoutLRToRenderable :: forall x yl yr . (Ord x, Ord yl, Ord yr)
                         => LayoutLR x yl yr -> Renderable (LayoutPick x yl yr)
    layoutLRToRenderable l = fillBackground (_layoutlr_background l)
                           $ gridToRenderable (layoutLRToGrid l)

    layoutLRToGrid :: forall x yl yr . (Ord x, Ord yl, Ord yr)
                         => LayoutLR x yl yr -> Grid (Renderable (LayoutPick x yl yr))
    layoutLRToGrid l = grid
      where
        grid = titleToRenderable lm (_layoutlr_title_style l) (_layoutlr_title l)
               `wideAbove`
               addMarginsToGrid (lm,lm,lm,lm) (layoutLRPlotAreaToGrid l)
               `aboveWide`
               renderLegendLR l (getLegendItemsLR l)
        lm = _layoutlr_margin l

    getLayoutLRXVals :: LayoutLR x yl yr -> [x]
    getLayoutLRXVals l = concatMap deEither $ _layoutlr_plots l
      where
        deEither :: Either (Plot x yl) (Plot x yr) -> [x]
        deEither (Left x)  = fst $ _plot_all_points x
        deEither (Right x) = fst $ _plot_all_points x

    -- | Extract all 'LegendItem's from the plots of a 'LayoutLR'.
    --   Left and right plot legend items are still separated.
    getLegendItemsLR :: LayoutLR x yl yr -> ([LegendItem],[LegendItem])
    getLegendItemsLR l = (
        concat [ _plot_legend p | (Left p ) <- _layoutlr_plots l ],
        concat [ _plot_legend p | (Right p) <- _layoutlr_plots l ]
        )

    -- | Render the given 'LegendItem's for a 'LayoutLR'.
    renderLegendLR :: LayoutLR x yl yr -> ([LegendItem],[LegendItem]) -> Renderable (LayoutPick x yl yr)
    renderLegendLR l (lefts,rights) = gridToRenderable g
      where
        g      = besideN [ tval $ mkLegend (_layoutlr_legend l) (_layoutlr_margin l) lefts
                         , weights (1,1) $ tval emptyRenderable
                         , tval $ mkLegend (_layoutlr_legend l) (_layoutlr_margin l) rights ]
        -- lm     = _layoutlr_margin l

    layoutLRPlotAreaToGrid :: forall x yl yr. (Ord x, Ord yl, Ord yr)
                           => LayoutLR x yl yr
                           -> Grid (Renderable (LayoutPick x yl yr))
    layoutLRPlotAreaToGrid l = buildGrid LayoutGridElements{
      lge_plots = mfill (_layoutlr_plot_background l) $ plotsToRenderable l,
      lge_taxis = (tAxis,_laxis_title $ _layoutlr_x_axis l, _laxis_title_style $ _layoutlr_x_axis l),
      lge_baxis = (bAxis,_laxis_title $ _layoutlr_x_axis l, _laxis_title_style $ _layoutlr_x_axis l),
      lge_laxis = (lAxis,_laxis_title $ _layoutlr_left_axis l, _laxis_title_style $ _layoutlr_left_axis l),
      lge_raxis = (rAxis,_laxis_title $ _layoutlr_right_axis l, _laxis_title_style $ _layoutlr_right_axis l),
      lge_margin = _layoutlr_margin l
      }
      where
        xvals =  [ x | (Left p)  <- _layoutlr_plots l, x <- fst $ _plot_all_points p]
              ++ [ x | (Right p) <- _layoutlr_plots l, x <- fst $ _plot_all_points p]
        yvalsL = [ y | (Left p)  <- _layoutlr_plots l, y <- snd $ _plot_all_points p]
        yvalsR = [ y | (Right p) <- _layoutlr_plots l, y <- snd $ _plot_all_points p]

        bAxis = mkAxis E_Bottom (overrideAxisVisibility l _layoutlr_x_axis _layoutlr_bottom_axis_visibility) xvals
        tAxis = mkAxis E_Top    (overrideAxisVisibility l _layoutlr_x_axis _layoutlr_top_axis_visibility   ) xvals
        lAxis = mkAxis E_Left   (overrideAxisVisibility l _layoutlr_left_axis  _layoutlr_left_axis_visibility ) yvalsL
        rAxis = mkAxis E_Right  (overrideAxisVisibility l _layoutlr_right_axis _layoutlr_right_axis_visibility) yvalsR
        axes = (bAxis,lAxis,tAxis,rAxis)

        plotsToRenderable llr = Renderable {
            minsize = return (0,0),
            render  = renderPlots llr
        }

        renderPlots :: LayoutLR x yl yr -> RectSize -> BackendProgram (PickFn (LayoutPick x yl yr))
        renderPlots llr sz@(w,h) = do
            unless (_layoutlr_grid_last llr) (renderGrids sz axes)
            withClipRegion (Rect (Point 0 0) (Point w h)) $
              mapM_ rPlot (_layoutlr_plots llr)
            when (_layoutlr_grid_last llr) (renderGrids sz axes)
            return pickfn
          where
            rPlot (Left  p) = renderSinglePlot sz bAxis lAxis p
            rPlot (Right p) = renderSinglePlot sz bAxis rAxis p

            xr = (0, w)
            yr = (h, 0)

            pickfn (Point x y) = do  -- Maybe monad
                xat <- mxat
                (yatL,yatR) <- myats
                return (LayoutPick_PlotArea (mapx xat x) (mapy yatL y) (mapy yatR y))
              where
                mxat = case (bAxis,tAxis) of
                    (Just at,_)       -> Just at
                    (_,Just at)       -> Just at
                    (Nothing,Nothing) -> Nothing
                myats = case (lAxis,rAxis) of
                    (Just at1,Just at2) -> Just (at1,at2)
                    (_,_)   -> Nothing
                mapx (AxisT _ _ rev ad) = _axis_tropweiv ad (optPairReverse rev xr)
                mapy (AxisT _ _ rev ad) = _axis_tropweiv ad (optPairReverse rev yr)

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

    -- | A layout with its y type hidden, so that it can be stacked
    --   with other layouts with differing y axis, but the same x axis.
    --   See 'StackedLayouts'.
    data StackedLayout x = forall y     . (Ord y)          => StackedLayout (Layout x y)
                           -- ^ A 'Layout' to stack.
                         | forall yl yr . (Ord yl, Ord yr) => StackedLayoutLR (LayoutLR x yl yr)
                           -- ^ A 'LayoutLR' to stack.

    -- | A container for a set of vertically 'StackedLayout's.
    --   The x axis of the different layouts will be aligned.
    data StackedLayouts x = StackedLayouts
      { _slayouts_layouts :: [StackedLayout x]
        -- ^ The stacked layouts from top (first element) to bottom (last element).
      , _slayouts_compress_legend :: Bool
        -- ^ If the different legends shall be combined in one legend at the bottom.
      }

    -- | A empty 'StackedLayout' with compressions applied.
    instance Default (StackedLayouts x) where
      def = StackedLayouts [] True

    instance Ord x => ToRenderable (StackedLayouts x) where
      toRenderable = renderStackedLayouts

    -- | Render several layouts with the same x-axis type and range,
    --   vertically stacked so that their origins and x-values are aligned.
    --
    --   The legends from all the charts may be optionally combined, and shown
    --   once on the bottom chart. See 'StackedLayouts' for further information.
    renderStackedLayouts :: forall x. (Ord x) => StackedLayouts x -> Renderable ()
    renderStackedLayouts (StackedLayouts{_slayouts_layouts=[]}) = emptyRenderable
    renderStackedLayouts slp@(StackedLayouts{_slayouts_layouts=sls@(sl1:_)}) = gridToRenderable g
      where
        g = fullOverlayUnder (fillBackground bg emptyRenderable)
          $ foldr (above.mkGrid) nullt (zip sls [0,1..])

        mkGrid :: (StackedLayout x, Int) -> Grid (Renderable ())
        mkGrid (sl, i)
            = titleR
              `wideAbove`
              addMarginsToGrid (lm,lm,lm,lm) (mkPlotArea usedAxis)
              `aboveWide`
              (if showLegend then legendR else emptyRenderable)
          where
            titleR = case sl of
                       StackedLayout l -> noPickFn $ titleToRenderable (_layout_margin l) (_layout_title_style l) (_layout_title l)
                       StackedLayoutLR l -> noPickFn $ titleToRenderable (_layoutlr_margin l) (_layoutlr_title_style l) (_layoutlr_title l)
            legendR = case sl of
                        StackedLayout l -> noPickFn $ renderLegend l $ fst legenditems
                        StackedLayoutLR l -> noPickFn $ renderLegendLR l legenditems

            legenditems = case (_slayouts_compress_legend slp,isBottomPlot) of
                (False,_) -> case sl of
                               StackedLayout l -> (getLegendItems l, [])
                               StackedLayoutLR l -> getLegendItemsLR l
                (True,True) -> allLegendItems
                (True,False) -> ([],[])

            mkPlotArea :: LayoutAxis x -> Grid (Renderable ())
            mkPlotArea axis = case sl of
              StackedLayout l -> fmap noPickFn
                               $ layoutPlotAreaToGrid
                               $ l { _layout_x_axis = axis }
              StackedLayoutLR l -> fmap noPickFn
                                 $ layoutLRPlotAreaToGrid
                                 $ l { _layoutlr_x_axis = axis }

            showLegend = not (null (fst legenditems)) || not (null (snd legenditems))

            isBottomPlot = i == length sls - 1

            lm = case sl of
              StackedLayout l -> _layout_margin l
              StackedLayoutLR l -> _layoutlr_margin l

            xAxis :: LayoutAxis x
            xAxis = case sl of
              StackedLayout l -> _layout_x_axis l
              StackedLayoutLR l -> _layoutlr_x_axis l

            usedAxis :: LayoutAxis x
            usedAxis = xAxis
              { _laxis_generate = const (_laxis_generate xAxis all_xvals) }

        bg = case sl1 of
               StackedLayout l -> _layout_background l
               StackedLayoutLR l -> _layoutlr_background l

        getXVals :: StackedLayout x -> [x]
        getXVals (StackedLayout l) = getLayoutXVals l
        getXVals (StackedLayoutLR l) = getLayoutLRXVals l

        all_xvals = concatMap getXVals sls

        allLegendItems = (concatMap (fst.legendItems) sls, concatMap (snd.legendItems) sls)

        legendItems :: StackedLayout x -> ([LegendItem], [LegendItem])
        legendItems (StackedLayout l)   = (getLegendItems l, [])
        legendItems (StackedLayoutLR l) = getLegendItemsLR l

        noPickFn :: Renderable a -> Renderable ()
        noPickFn = mapPickFn (const ())

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

    addMarginsToGrid :: (Double,Double,Double,Double) -> Grid (Renderable a)
                     -> Grid (Renderable a)
    addMarginsToGrid (t,b,l,r) g = aboveN [
         besideN [er, ts, er],
         besideN [ls, g,  rs],
         besideN [er, bs, er]
      ]
      where
        er = empty
        ts = tval $ spacer (0,t)
        ls = tval $ spacer (l,0)
        bs = tval $ spacer (0,b)
        rs = tval $ spacer (r,0)

    titleToRenderable :: Double -> FontStyle -> String -> Renderable (LayoutPick x yl yr)
    titleToRenderable _  _  "" = emptyRenderable
    titleToRenderable lm fs s = addMargins (lm/2,0,0,0) (mapPickFn LayoutPick_Title title)
      where
        title = label fs HTA_Centre VTA_Centre s

    mkLegend :: Maybe LegendStyle -> Double -> [LegendItem] -> Renderable (LayoutPick x yl yr)
    mkLegend mls lm vals = case mls of
        Nothing -> emptyRenderable
        Just ls ->  case filter ((/="").fst) vals of
            []  -> emptyRenderable ;
            lvs -> addMargins (0,lm,lm,lm) $
                       mapPickFn LayoutPick_Legend $ legendToRenderable (Legend ls lvs)

    data LayoutGridElements x yl yr = LayoutGridElements {
      lge_plots :: Renderable (LayoutPick x yl yr),

      lge_taxis :: (Maybe (AxisT x),String,FontStyle),
      lge_baxis :: (Maybe (AxisT x),String,FontStyle),
      lge_laxis :: (Maybe (AxisT yl),String,FontStyle),
      lge_raxis :: (Maybe (AxisT yr),String,FontStyle),

      lge_margin :: Double
    }

    buildGrid :: (Ord x, Ord yl, Ord yr) => LayoutGridElements x yl yr -> Grid (Renderable (LayoutPick x yl yr))
    buildGrid lge = layer2 `overlay` layer1
      where
        layer1 = aboveN
             [ besideN [er,     er,  er,    er   ]
             , besideN [er,     er,  er,    weights (1,1) plots ]
             ]

        layer2 = aboveN
             [ besideN [er,     er,  tl,    taxis,  tr,    er,  er       ]
             , besideN [ltitle, lam, laxis, er,     raxis, ram, rtitle   ]
             , besideN [er,     er,  bl,    baxis,  br,    er,  er       ]
             , besideN [er,     er,  er,    btitle, er,    er,  er       ]
             ]

        er = tval emptyRenderable

        plots = tval $ lge_plots lge

        (tdata,_,_)         = lge_taxis lge
        (bdata,blbl,bstyle) = lge_baxis lge
        (ldata,llbl,lstyle) = lge_laxis lge
        (rdata,rlbl,rstyle) = lge_raxis lge

        -- (ttitle,_) = mktitle HTA_Centre VTA_Bottom   0 tlbl tstyle LayoutPick_XTopAxisTitle
        (btitle,_) = mktitle HTA_Centre VTA_Top      0 blbl bstyle LayoutPick_XBottomAxisTitle
        (ltitle,lam) = mktitle HTA_Right  VTA_Centre 270 llbl lstyle LayoutPick_YLeftAxisTitle
        (rtitle,ram) = mktitle HTA_Left   VTA_Centre 270 rlbl rstyle LayoutPick_YRightAxisTitle

        baxis = tval $ maybe emptyRenderable
                             (mapPickFn LayoutPick_XBottomAxis . axisToRenderable) bdata
        taxis = tval $ maybe emptyRenderable
                             (mapPickFn LayoutPick_XTopAxis . axisToRenderable) tdata
        laxis = tval $ maybe emptyRenderable
                             (mapPickFn LayoutPick_YLeftAxis . axisToRenderable) ldata
        raxis = tval $ maybe emptyRenderable
                             (mapPickFn LayoutPick_YRightAxis . axisToRenderable) rdata

        tl = tval $ axesSpacer fst tdata fst ldata
        bl = tval $ axesSpacer fst bdata snd ldata
        tr = tval $ axesSpacer snd tdata fst rdata
        br = tval $ axesSpacer snd bdata snd rdata

        mktitle :: HTextAnchor -> VTextAnchor
                -> Double
                -> String -> FontStyle
                -> (String -> LayoutPick x yl yr)
                -> ( Grid (Renderable (LayoutPick x yl yr))
                   , Grid (Renderable (LayoutPick x yl yr)) )
        mktitle ha va rot lbl style pf = if lbl == "" then (er,er) else (labelG,gapG)
          where
            labelG = tval $ mapPickFn pf $ rlabel style ha va rot lbl
            gapG = tval $ spacer (lge_margin lge,0)

    -- | Render the grids of the given axis to a plot area of given size.
    renderGrids :: RectSize -> (Maybe (AxisT x), Maybe (AxisT yl), Maybe (AxisT x), Maybe (AxisT yr)) -> BackendProgram ()
    renderGrids sz (bAxis, lAxis, tAxis, rAxis) = do
      maybeM () (renderAxisGrid sz) tAxis
      maybeM () (renderAxisGrid sz) bAxis
      maybeM () (renderAxisGrid sz) lAxis
      maybeM () (renderAxisGrid sz) rAxis

    -- | Swap the contents of the pair depending on the flag.
    optPairReverse :: Bool -> (a,a) -> (a,a)
    optPairReverse rev (a,b) = if rev then (b,a) else (a,b)

    -- | Render a single set of plot data onto a plot area of given size using
    --   the given x and y axis.
    renderSinglePlot :: RectSize -> Maybe (AxisT x) -> Maybe (AxisT y) -> Plot x y -> BackendProgram ()
    renderSinglePlot (w, h) (Just (AxisT _ _ xrev xaxis)) (Just (AxisT _ _ yrev yaxis)) p =
      let xr = optPairReverse xrev (0, w)
          yr = optPairReverse yrev (h, 0)
          -- yrange = if yrev then (0, h) else (h, 0)
          pmfn (x,y) = Point (mapv xr (_axis_viewport xaxis xr) x)
                             (mapv yr (_axis_viewport yaxis yr) y)
          mapv lims _ LMin       = fst lims
          mapv lims _ LMax       = snd lims
          mapv _    f (LValue v) = f v
      in _plot_render p pmfn
    renderSinglePlot _ _ _ _ = return ()

    axesSpacer :: (Ord x, Ord y)
               => ((Double, Double) -> Double) -> Maybe (AxisT x)
               -> ((Double, Double) -> Double) -> Maybe (AxisT y)
               -> Renderable a
    axesSpacer f1 a1 f2 a2 = embedRenderable $ do
        oh1 <- maybeM (0,0) axisOverhang a1
        oh2 <- maybeM (0,0) axisOverhang a2
        return (spacer (f1 oh1, f2 oh2))

    -- | Construct a axis for the given edge using the attributes
    --   from a 'LayoutAxis' the given values.
    mkAxis :: RectEdge -> LayoutAxis z -> [z] -> Maybe (AxisT z)
    mkAxis edge laxis vals = case axisVisible of
        False -> Nothing
        True  -> Just $ AxisT edge style rev adata
      where
        style = _laxis_style laxis
        rev   = _laxis_reverse laxis
        adata = _laxis_override laxis (_laxis_generate laxis vals)
        vis   = _axis_visibility adata
        axisVisible = _axis_show_labels vis || _axis_show_line vis || _axis_show_ticks vis

    -- | Override the visibility of a selected axis with the selected 'AxisVisibility'.
    overrideAxisVisibility :: layout
                           -> (layout -> LayoutAxis z)
                           -> (layout -> AxisVisibility)
                           -> LayoutAxis z
    overrideAxisVisibility ly selAxis selVis =
      let vis = selVis ly
      in (selAxis ly) { _laxis_override = (\ad -> ad { _axis_visibility = vis })
                                        . _laxis_override (selAxis ly)
                      }

    mfill :: Maybe FillStyle -> Renderable a -> Renderable a
    mfill Nothing   = id
    mfill (Just fs) = fillBackground fs

    -- | Empty 'LayoutLR' without title and plots. The background is white and
    --   the grid is drawn beneath all plots. There will be a legend. The top
    --   axis will not be visible.
    instance (PlotValue x, PlotValue y1, PlotValue y2) => Default (LayoutLR x y1 y2) where
      def = LayoutLR
        { _layoutlr_background      = solidFillStyle $ opaque white
        , _layoutlr_plot_background = Nothing

        , _layoutlr_title           = ""
        , _layoutlr_title_style     = def { _font_size   = 15
                                          , _font_weight = FontWeightBold }

        , _layoutlr_x_axis                 = def
        , _layoutlr_top_axis_visibility    = def { _axis_show_line   = False
                                                 , _axis_show_ticks  = False
                                                 , _axis_show_labels = False }
        , _layoutlr_bottom_axis_visibility = def

        , _layoutlr_left_axis           = def
        , _layoutlr_left_axis_visibility  = def
        , _layoutlr_right_axis          = def
        , _layoutlr_right_axis_visibility = def

        , _layoutlr_plots      = []

        , _layoutlr_legend          = Just def
        , _layoutlr_margin          = 10
        , _layoutlr_grid_last       = False
        }

    instance PlotValue t => Default (LayoutAxis t) where
      def = LayoutAxis
        { _laxis_title_style = def { _font_size=10 }
        , _laxis_title       = ""
        , _laxis_style       = def
        , _laxis_generate    = autoAxis
        , _laxis_override    = id
        , _laxis_reverse     = False
        }

    ----------------------------------------------------------------------
    -- Template haskell to derive an instance of Data.Accessor.Accessor
    -- for each field.
    $( makeLenses ''Layout )
    $( makeLenses ''LayoutLR )
    $( makeLenses ''LayoutAxis )
    $( makeLenses ''StackedLayouts )

    -- | Setter to update all axis styles on a `Layout`
    layout_axes_styles :: Setter' (Layout x y) AxisStyle
    layout_axes_styles = sets $ \af ->
        (layout_x_axis . laxis_style %~ af) .
        (layout_y_axis . laxis_style %~ af)

    -- | Setter to update all the axes title styles on a `Layout`
    layout_axes_title_styles :: Setter' (Layout x y) FontStyle
    layout_axes_title_styles = sets $ \af ->
        (layout_x_axis . laxis_title_style %~ af) .
        (layout_y_axis . laxis_title_style %~ af)

    -- | Setter to update all the font styles on a `Layout`
    layout_all_font_styles :: Setter' (Layout x y) FontStyle
    layout_all_font_styles = sets $ \af ->
        (layout_axes_title_styles %~ af) .
        (layout_x_axis . laxis_style . axis_label_style %~ af) .
        (layout_y_axis . laxis_style . axis_label_style %~ af) .
        (layout_legend . _Just . legend_label_style %~ af) .
        (layout_title_style %~ af)

    -- | Setter to update the foreground color of core chart elements on a `Layout`
    layout_foreground ::  Setter' (Layout x y) (AlphaColour Double)
    layout_foreground = sets $ \af ->
        (layout_all_font_styles . font_color %~ af) .
        (layout_axes_styles . axis_line_style . line_color %~ af)

    -- | Setter to update all axis styles on a `LayoutLR`
    layoutlr_axes_styles :: Setter' (LayoutLR x y1 y2) AxisStyle
    layoutlr_axes_styles = sets $ \af ->
        (layoutlr_x_axis . laxis_style %~ af) .
        (layoutlr_left_axis . laxis_style %~ af) .
        (layoutlr_right_axis . laxis_style %~ af)

    -- | Setter to update all the axes title styles on a `LayoutLR`
    layoutlr_axes_title_styles :: Setter' (LayoutLR x y1 y2) FontStyle
    layoutlr_axes_title_styles = sets $ \af ->
        (layoutlr_x_axis . laxis_title_style %~ af) .
        (layoutlr_left_axis . laxis_title_style %~ af) .
        (layoutlr_right_axis . laxis_title_style %~ af)

    -- | Setter to update all the font styles on a `LayoutLR`
    layoutlr_all_font_styles :: Setter' (LayoutLR x y1 y2) FontStyle
    layoutlr_all_font_styles = sets $ \af ->
        (layoutlr_axes_title_styles %~ af) .
        (layoutlr_x_axis . laxis_style . axis_label_style %~ af) .
        (layoutlr_left_axis . laxis_style . axis_label_style %~ af) .
        (layoutlr_right_axis . laxis_style . axis_label_style %~ af) .
        (layoutlr_legend . _Just . legend_label_style %~ af) .
        (layoutlr_title_style %~ af)

    -- | Setter to update the foreground color of core chart elements on a `LayoutLR`
    layoutlr_foreground ::  Setter' (LayoutLR x y1 y2) (AlphaColour Double)
    layoutlr_foreground = sets $ \af ->
        (layoutlr_all_font_styles . font_color %~ af) .
        (layoutlr_axes_styles . axis_line_style . line_color %~ af)