    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Graphics.Rendering.Chart.Plot.Lines
    -- Copyright   :  (c) Tim Docker 2006, 2014
    -- License     :  BSD-style (see chart/COPYRIGHT)
    --
    -- Line plots
    --
    {-# LANGUAGE TemplateHaskell #-}

    module Graphics.Rendering.Chart.Plot.Lines(
        PlotLines(..),
        defaultPlotLineStyle,
        hlinePlot,
        vlinePlot,

        plot_lines_title,
        plot_lines_style,
        plot_lines_values,
        plot_lines_limit_values,
    ) where

    import Control.Lens
    import Graphics.Rendering.Chart.Geometry
    import Graphics.Rendering.Chart.Drawing
    import Graphics.Rendering.Chart.Plot.Types
    import Data.Colour (opaque)
    import Data.Colour.Names (blue)
    import Data.Default.Class

    -- | Value defining a series of (possibly disjointed) lines,
    --   and a style in which to render them.
    data PlotLines x y = PlotLines {
        _plot_lines_title        :: String,
        _plot_lines_style        :: LineStyle,

        -- | The lines to be plotted
        _plot_lines_values       :: [[(x,y)]],

        -- | Additional lines to be plotted, specified using
        -- the Limit type to allow referencing the edges of
        -- the plot area.
        _plot_lines_limit_values :: [[(Limit x, Limit y)]]
    }

    instance ToPlot PlotLines where
        toPlot p = Plot {
            _plot_render     = renderPlotLines p,
            _plot_legend     = [(_plot_lines_title p, renderPlotLegendLines p)],
            _plot_all_points = ( map fst pts ++ xs, map snd pts ++ ys )
        }
          where
            pts = concat (_plot_lines_values p)
            xs = [ x | (LValue x,_) <- concat (_plot_lines_limit_values p)]
            ys = [ y | (_,LValue y) <- concat (_plot_lines_limit_values p)]

    renderPlotLines :: PlotLines x y -> PointMapFn x y -> BackendProgram ()
    renderPlotLines p pmap =
      withLineStyle (_plot_lines_style p) $ do
        mapM_ (drawLines (mapXY pmap)) (_plot_lines_values p)
        mapM_ (drawLines pmap) (_plot_lines_limit_values p)
      where
        drawLines mapfn pts = alignStrokePoints (map mapfn pts) >>= strokePointPath

    renderPlotLegendLines :: PlotLines x y -> Rect -> BackendProgram ()
    renderPlotLegendLines p (Rect p1 p2) =
      withLineStyle (_plot_lines_style p) $ do
        let y = (p_y p1 + p_y p2) / 2
        ps <- alignStrokePoints [Point (p_x p1) y, Point (p_x p2) y]
        strokePointPath ps

    defaultPlotLineStyle :: LineStyle
    defaultPlotLineStyle = (solidLine 1 $ opaque blue){
         _line_cap  = LineCapRound,
         _line_join = LineJoinRound
     }

    instance Default (PlotLines x y) where
      def = PlotLines
        { _plot_lines_title        = ""
        , _plot_lines_style        = defaultPlotLineStyle
        , _plot_lines_values       = []
        , _plot_lines_limit_values = []
        }

    -- | Helper function to plot a single horizontal line.
    hlinePlot :: String -> LineStyle -> b -> Plot a b
    hlinePlot t ls v = toPlot def {
        _plot_lines_title        = t,
        _plot_lines_style        = ls,
        _plot_lines_limit_values = [[(LMin, LValue v),(LMax, LValue v)]]
        }

    -- | Helper function to plot a single vertical line.
    vlinePlot :: String -> LineStyle -> a -> Plot a b
    vlinePlot t ls v = toPlot def {
        _plot_lines_title        = t,
        _plot_lines_style        = ls,
        _plot_lines_limit_values = [[(LValue v,LMin),(LValue v,LMax)]]
        }

    $( makeLenses ''PlotLines )