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

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

        plot_lines_title,
        plot_lines_style,
        plot_lines_values,
        plot_lines_limit_values,
    ) where

    import Data.Accessor.Template
    import qualified Graphics.Rendering.Cairo as C
    import Graphics.Rendering.Chart.Types
    import Graphics.Rendering.Chart.Renderable
    import Graphics.Rendering.Chart.Plot.Types
    import Data.Colour (opaque)
    import Data.Colour.Names (black, blue)

    -- | 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_        :: CairoLineStyle,

        -- | 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 -> CRender ()
    renderPlotLines p pmap = preserveCState $ do
        setLineStyle (plot_lines_style_ p)
        mapM_ (drawLines (mapXY pmap)) (plot_lines_values_ p)
        mapM_ (drawLines pmap) (plot_lines_limit_values_ p)
      where
        drawLines mapfn pts = strokePath (map mapfn pts)

    renderPlotLegendLines :: PlotLines x y -> Rect -> CRender ()
    renderPlotLegendLines p r@(Rect p1 p2) = preserveCState $ do
        setLineStyle (plot_lines_style_ p)
        let y = (p_y p1 + p_y p2) / 2
        strokePath [Point (p_x p1) y, Point (p_x p2) y]

    defaultPlotLineStyle :: CairoLineStyle
    defaultPlotLineStyle = (solidLine 1 $ opaque blue){
         line_cap_  = C.LineCapRound,
         line_join_ = C.LineJoinRound
     }

    defaultPlotLines :: PlotLines x y
    defaultPlotLines = 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 -> CairoLineStyle -> b -> Plot a b
    hlinePlot t ls v = toPlot defaultPlotLines {
        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 -> CairoLineStyle -> a -> Plot a b
    vlinePlot t ls v = toPlot defaultPlotLines {
        plot_lines_title_        = t,
        plot_lines_style_        = ls,
        plot_lines_limit_values_ = [[(LValue v,LMin),(LValue v,LMax)]]
        }

    ----------------------------------------------------------------------
    -- Template haskell to derive an instance of Data.Accessor.Accessor
    -- for each field.
    $( deriveAccessors ''PlotLines )