{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Graphics.Rendering.Plot.Figure
-- Copyright   :  (c) A. V. H. McPhail 2010
-- License     :  BSD3
--
-- Maintainer  :  haskell.vivian.mcphail <at> gmail <dot> com
-- Stability   :  provisional
-- Portability :  portable
--
-- Creation and manipulation of 'Figure's
--
-- The same problem of leaked instances as at <http://hackage.haskell.org/packages/archive/graphviz/2999.10.0.1/doc/html/Data-GraphViz-Commands.html#t%3AGraphvizCanvas> occurs here.
--
--
-- /with/, /set/, /clear/, /new/, and /add/ are the operations that can
-- be performed on various elements of a figure.
-- 
-- /glib/\//data-accessor/ abstractions (verbs/modifiers) are planned for future implementations

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

module Graphics.Rendering.Plot.Figure (
                                       -- * Top level operation
                                       Figure(), FigureState()
                                       -- * Default options
                                      , withTextDefaults
                                      , withLineDefaults 
                                      , withPointDefaults 
                                      , withBarDefaults 
                                      -- * Figures
                                      , newFigure
                                      -- ** Formatting
                                      , setFigurePadding
                                      , withTitle
                                      , withSubTitle
                                      , setPlots
                                      , withPlot, withPlots  
                                      -- * Sub-plots
                                      ,  Plot()
                                      -- ** Plot elements 
                                      , Border
                                      , setBorder
                                      , setPlotPadding
                                      , withHeading
                                      -- ** Series data
                                      , Function(), Series(), MinMaxSeries(), ErrorSeries()
                                      , Surface()
                                      , SeriesLabel()
                                      , Abscissa(), Ordinate(), Dataset()
                                      , FormattedSeries(), SeriesType(..)
                                      , line, point, linepoint
                                      , impulse, step
                                      , area
                                      , bar
                                      , hist
                                      , candle, whisker
                                      , setDataset
                                      -- ** Plot type
                                      , setSeriesType
                                      , setAllSeriesTypes
                                      -- ** Formatting
                                      , PlotFormats()
                                      , withSeriesFormat
                                      , withAllSeriesFormats
                                      -- * Range
                                      , Scale(..)
                                      , setRange
                                      , setRangeFromData
                                      -- * Axes
                                      , Axis
                                      , AxisType(..),AxisSide(..),AxisPosn(..)
                                      , clearAxes
                                      , clearAxis
                                      , addAxis
                                      , withAxis
                                      -- * Legend
                                      , Legend
                                      , LegendBorder
                                      , LegendLocation(..), LegendOrientation(..)
                                      , clearLegend
                                      , setLegend
                                      , withLegendFormat
                                      -- ** Formatting
                                      , Tick(..), TickValues, GridLines
                                      , setTicks
                                      , setGridlines
                                      , setTickLabelFormat
                                      , withAxisLabel
                                      , withAxisLine
                                       -- * Lines
                                      , Line(), LineFormat()
                                      , DashStyle,Dash(..),LineWidth
                                      , clearLineFormat
                                      , setDashStyle
                                      , setLineWidth
                                      , setLineColour
                                      -- * Points
                                      , Point(), PointFormat()
                                      , Glyph(..)
                                      , PointSize
                                      , setGlyph
                                      , setPointSize
                                      , setPointColour
                                      -- * Bars
                                      , Bar(), BarFormat()
                                      , clearBarFormat
                                      , setBarWidth
                                      , setBarColour
                                      , setBarBorderWidth
                                      , setBarBorderColour
                                      -- * Text labels
                                      , Text()
                                      , FontFamily,FontSize,Color
                                      -- | A text element must exist for formatting to work
                                      , clearText
                                      , clearTextFormat
                                      , setText
                                      , setFontFamily
                                      , setFontStyle
                                      , setFontVariant
                                      , setFontWeight
                                      , setFontStretch
                                      , setFontSize
                                      , setFontColour
                                      ) where

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

--import Data.Packed.Vector
--import Numeric.LinearAlgebra.Linear

--import Data.Word
--import Data.Colour.SRGB
import Data.Colour.Names

import qualified Data.Array.IArray as A

--import qualified Graphics.Rendering.Cairo as C
--import qualified Graphics.Rendering.Pango as P

--import Control.Monad.State
--import Control.Monad.Reader

import Prelude hiding(min,max)

import Graphics.Rendering.Plot.Figure.Text
import Graphics.Rendering.Plot.Figure.Line
import Graphics.Rendering.Plot.Figure.Point
import Graphics.Rendering.Plot.Figure.Bar
import Graphics.Rendering.Plot.Figure.Plot

import Graphics.Rendering.Plot.Types
import Graphics.Rendering.Plot.Defaults

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

-- | perform some actions on the text defaults, must be run before other text element modifications
withTextDefaults :: Text () -> Figure ()
withTextDefaults m = do
                     o <- getDefaults
                     let to' = _textoptions o
                     let (FontText to _) = execText m to' (FontText to' "")    
                     modifyDefaults $ \s -> s { _textoptions = to }

-- | perform some actions on the line defaults, must be run before other line element modifications
withLineDefaults :: Line () -> Figure ()
withLineDefaults m = do
                     o <- getDefaults
                     let lo' = _lineoptions o
                     let (TypeLine lo _) = execLine m lo' (TypeLine lo' black)
                     modifyDefaults $ \s -> s { _lineoptions = lo }
                     
-- | perform some actions on the point defaults, must be run before other point modifications
withPointDefaults :: Point () -> Figure ()
withPointDefaults m = do
                     o <- getDefaults
                     let po' = _pointoptions o
                     let (FullPoint po _) = execPoint m po' (FullPoint po' defaultGlyph)
                     modifyDefaults $ \s -> s { _pointoptions = po }

-- | perform some actions on the bar defaults, must be run before other point modifications
withBarDefaults :: Bar () -> Figure ()
withBarDefaults m = do
                    o <- getDefaults
                    let bo' = _baroptions o
                    let (TypeBar bo _) = execBar m bo' (TypeBar bo' black)
                    modifyDefaults $ \s -> s { _baroptions = bo }
                     
-----------------------------------------------------------------------------

-- | create a new blank 'Figure'
newFigure :: Figure ()
newFigure = putFigure $ Figure defaultFigurePadding NoText NoText
                               (A.listArray ((1,1),(1,1)) [Nothing]) 
{-
newLineFigure :: DataSeries                      -- ^ the y series
              -> FigureData
newLineFigure d@(DS_1toN _ _) = let ((xmin,xmax),(ymin,ymax)) = calculateRanges d
                                    plot = Plot False defaultPlotPadding NoText
                                           (defaultRanges xmin xmax ymin ymax) 
                                           [defaultXAxis,defaultYAxis] 
                                           Nothing Line d []
                                in Figure defaultFigurePadding NoText NoText
                                       (A.listArray ((1,1),(1,1)) [Just plot]) 
-}
{-
-- | create a new 'Figure'
newFigure :: PlotType -> DataSeries -> Figure ()
newFigure Line   d@(DS_1toN _ _) = putFigure $ newLineFigure d
--newFigure _      _               = error "Figure type not implemented"
-}


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

-- | set the padding of the figure
setFigurePadding :: Double -> Double -> Double -> Double -> Figure ()
setFigurePadding l r b t = modifyFigure $ \s -> s { _fig_pads = Padding l r b t }

-- | operate on the title
withTitle :: Text () -> Figure ()
withTitle m = do
              o <- getDefaults
              modifyFigure $ \s -> s { _title = execText m (_textoptions o) (_title s) }

-- | operate on the sub-title
withSubTitle :: Text () -> Figure ()
withSubTitle m = do
              o <- getDefaults
              modifyFigure $ \s -> s { _subtitle = execText m (_textoptions o) (_title s) }

-- | set the shape of the plots, losing all current plots
setPlots :: Int      -- ^ rows
         -> Int      -- ^ columns
         -> Figure ()
setPlots r c = modifyFigure $ \s -> s { _plots = A.listArray ((1,1),(r,c)) (replicate (r*c) Nothing) }

-- | perform some actions on the specified subplot
withPlot :: (Int,Int) -> Plot () -> Figure ()
withPlot i m = do
                  o <- getDefaults
                  s <- getSupplies
                  modifyFigure $ \p -> p { _plots = let plots = _plots p
                                                        plot' = plots A.! i
                                                        plot = case plot' of
                                                                          Nothing -> emptyPlot
                                                                          Just p' -> p'
                                                    -- we revert supplies to the original here
                                                    -- since we might want the same colour
                                                    -- order for all plots 
                                                    -- HOWEVER: need a better execPlot group
                                                    in plots A.// [(i,Just $ execPlot m s o plot)] }
               
-- | perform some actions all subplots
withPlots :: Plot () -> Figure ()
withPlots m = do
                 o <- getDefaults
                 s <- getSupplies
                 modifyFigure $ \p -> p { _plots = let plots = _plots p
                                                       plot p' = case p' of
                                                                         Nothing  -> emptyPlot
                                                                         Just p'' -> p''
                                                   in plots A.// map (\(i,e) -> (i,Just $ execPlot m s o (plot e))) (A.assocs plots) }
               
-----------------------------------------------------------------------------

