    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Graphics.Rendering.Chart.Plot.Pie
    -- Copyright   :  (c) Tim Docker 2008
    -- License     :  BSD-style (see chart/COPYRIGHT)
    --
    -- A  basic pie chart.
    --
    -- Pie charts are handled different to other plots, in that they
    -- have their own layout, and can't be composed with other plots. A
    -- pie chart is rendered with code in the following form:
    --
    -- @
    -- values :: [PieItem]
    -- values = [...]
    -- layout :: PieLayout
    -- layout = pie_plot ^: pie_data ^= values
    --        $ defaultPieLayout
    -- renderable = toRenderable layout
    -- @

    {-# OPTIONS_GHC -XTemplateHaskell #-}

    module Graphics.Rendering.Chart.Plot.Pie(
        PieLayout(..),
        PieChart(..),
        PieItem(..),
        defaultPieLayout,
        defaultPieChart,
        defaultPieItem,

        pie_title,
        pie_title_style,
        pie_plot,
        pie_background,
        pie_margin,
        pie_data,
        pie_colors,
        pie_label_style,
        pie_label_line_style,
        pie_start_angle,
        pitem_label,
        pitem_offset,
        pitem_value,

    ) where
    -- original code thanks to Neal Alexander

    import qualified Graphics.Rendering.Cairo as C

    import Data.List
    import Data.Bits
    import Data.Accessor.Template
    import Data.Colour
    import Data.Colour.Names (black, white)
    import Control.Monad

    import Graphics.Rendering.Chart.Types
    import Graphics.Rendering.Chart.Legend
    import Graphics.Rendering.Chart.Renderable
    import Graphics.Rendering.Chart.Grid
    import Graphics.Rendering.Chart.Plot.Types

    data PieLayout = PieLayout {
       pie_title_       :: String,
       pie_title_style_ :: CairoFontStyle,
       pie_plot_        :: PieChart,
       pie_background_  :: CairoFillStyle,
       pie_margin_      :: Double
    }

    data PieChart = PieChart {
       pie_data_             :: [PieItem],
       pie_colors_           :: [AlphaColour Double],
       pie_label_style_      :: CairoFontStyle,
       pie_label_line_style_ :: CairoLineStyle,
       pie_start_angle_      :: Double

    }

    data PieItem = PieItem {
       pitem_label_  :: String,
       pitem_offset_ :: Double,
       pitem_value_  :: Double
    }

    defaultPieChart :: PieChart
    defaultPieChart = PieChart {
        pie_data_             = [],
        pie_colors_           = defaultColorSeq,
        pie_label_style_      = defaultFontStyle,
        pie_label_line_style_ = solidLine 1 $ opaque black,
        pie_start_angle_      = 0
    }

    defaultPieItem :: PieItem
    defaultPieItem = PieItem "" 0 0

    defaultPieLayout :: PieLayout
    defaultPieLayout = PieLayout {
        pie_background_  = solidFillStyle $ opaque white,
        pie_title_       = "",
        pie_title_style_ = defaultFontStyle{ font_size_   = 15
                                           , font_weight_ = C.FontWeightBold },
        pie_plot_        = defaultPieChart,
        pie_margin_      = 10
    }

    instance ToRenderable PieLayout where
        toRenderable p = fillBackground (pie_background_ p) (
           gridToRenderable $ aboveN
             [ tval $ addMargins (lm/2,0,0,0) (setPickFn nullPickFn title)
             , weights (1,1) $ tval $ addMargins (lm,lm,lm,lm)
                                                 (toRenderable $ pie_plot_ p)
             ] )
          where
            title = label (pie_title_style_ p) HTA_Centre VTA_Top (pie_title_ p)
            lm    = pie_margin_ p

    instance ToRenderable PieChart where
        toRenderable p = Renderable {
          minsize = minsizePie p,
          render  = renderPie p
        }

    extraSpace p = do
        textSizes <- mapM textSize (map pitem_label_ (pie_data_ p))
        let maxw  = foldr (max.fst) 0 textSizes
        let maxh  = foldr (max.snd) 0 textSizes
        let maxo  = foldr (max.pitem_offset_) 0 (pie_data_ p)
        let extra = label_rgap + label_rlength + maxo
        return (extra + maxw, extra + maxh )

    minsizePie p = do
        (extraw,extrah) <- extraSpace p
        return (extraw * 2, extrah * 2)

    renderPie p (w,h) = do
        (extraw,extrah) <- extraSpace p
        let (w,h)  = (p_x p2 - p_x p1, p_y p2 - p_y p1)
        let center = Point (p_x p1 + w/2)  (p_y p1 + h/2)
        let radius = (min (w - 2*extraw) (h - 2*extrah)) / 2

        foldM_ (paint center radius) (pie_start_angle_ p)
               (zip (pie_colors_ p) content)
        return nullPickFn

        where
            p1 = Point 0 0
            p2 = Point w h
            content = let total = sum (map pitem_value_ (pie_data_ p))
                      in [ pi{pitem_value_=pitem_value_ pi/total}
                         | pi <- pie_data_ p ]

            paint :: Point -> Double -> Double -> (AlphaColour Double, PieItem)
                     -> CRender Double
            paint center radius a1 (color,pitem) = do
                let ax     = 360.0 * (pitem_value_ pitem)
                let a2     = a1 + (ax / 2)
                let a3     = a1 + ax
                let offset = pitem_offset_ pitem

                pieSlice (ray a2 offset) a1 a3 color
                pieLabel (pitem_label_ pitem) a2 offset

                return a3

                where
                    pieLabel :: String -> Double -> Double -> CRender ()
                    pieLabel name angle offset = do
                        setFontStyle (pie_label_style_ p)
                        setLineStyle (pie_label_line_style_ p)

                        moveTo (ray angle (radius + label_rgap+offset))
                        let p1 = ray angle (radius+label_rgap+label_rlength+offset)
                        lineTo p1
                        (tw,th) <- textSize name
                        let (offset,anchor) = if angle < 90 || angle > 270
                                              then ((0+),HTA_Left)
                                              else ((0-),HTA_Right)
                        c $ C.relLineTo (offset (tw + label_rgap)) 0
                        c $ C.stroke

                        let p2 = p1 `pvadd` (Vector (offset label_rgap) 0)
                        drawText anchor VTA_Bottom p2 name

                    pieSlice :: Point -> Double -> Double -> AlphaColour Double
                                -> CRender ()
                    pieSlice (Point x y) a1 a2 color = c $ do
                        C.newPath
                        C.arc x y radius (radian a1) (radian a2)
                        C.lineTo x y
                        C.lineTo x y
                        C.closePath

                        setSourceColor color
                        C.fillPreserve
                        C.setSourceRGBA 1 1 1 0.1

                        C.stroke

                    ray :: Double -> Double -> Point
                    ray angle r = Point x' y'
                      where
                        x'   = x + (cos' * x'')
                        y'   = y + (sin' * x'')
                        cos' = (cos . radian) angle
                        sin' = (sin . radian) angle
                        x''  = ((x + r) - x)
                        x    = p_x center
                        y    = p_y center

                    radian = (*(pi / 180.0))

    label_rgap = 5
    label_rlength = 15

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