module Graphics.Gnuplot.Private.Plot where

import qualified Data.Monoid.State as State
import Data.Monoid (Monoid, mempty, mappend, )

import qualified Graphics.Gnuplot.Display as Display
import qualified Graphics.Gnuplot.Private.FrameOptionSet as OptionSet
import qualified Graphics.Gnuplot.Private.Graph as Graph
import Graphics.Gnuplot.Utility (quote, commaConcat, )

import Data.Maybe (mapMaybe, )


{- |
Plots can be assembled using 'mappend' or 'mconcat'.
-}
newtype T graph = Cons (State.T Int [File graph])

{-
Could also be implemented with Control.Monad.Trans.State:
mappend = liftA2 mappend
-}
instance Monoid (T graph) where
   mempty = Cons mempty
   mappend (Cons s0) (Cons s1) =
      Cons (mappend s0 s1)


withUniqueFile :: String -> [graph] -> T graph
withUniqueFile content graphs =
   Cons (State.Cons $ \n ->
      ([File (tmpFileStem ++ show n ++ ".csv") (Just content) graphs],
       succ n))

fromGraphs :: FilePath -> [graph] -> T graph
fromGraphs name gs =
   Cons (State.pure [File name Nothing gs])


data File graph =
   File {
      filename_ :: FilePath,
      content_ :: Maybe String,
      graphs_ :: [graph]
   }

writeData :: File graph -> IO ()
writeData (File fn cont _) =
   maybe (return ()) (writeFile fn) cont


tmpFileStem :: FilePath
tmpFileStem = "curve"

{-
tmpFile :: FilePath
tmpFile = tmpFileStem ++ ".csv"
-}


instance Functor T where
   fmap f (Cons mp) =
      Cons $
      fmap (map (\file -> file{graphs_ = map f $ graphs_ file}))
      mp

{- |
In contrast to the Display.toScript method instantiation
this function leaves the options,
and thus can be used to write the Display.toScript instance for Frame.
-}
toScript :: Graph.C graph => T graph -> Display.Script
toScript p@(Cons mp) =
   Display.Script $ State.Cons $
      \(n0, opts) ->
         let (blocks, n1) = State.run mp n0
             files =
                mapMaybe
                   (\blk -> fmap (Display.File (filename_ blk)) (content_ blk))
                   blocks
             graphs =
                concatMap (\blk ->
                   map (\gr ->
                           quote (filename_ blk) ++ " " ++
                           Graph.toString gr) $ graphs_ blk) $
                   blocks
         in  (Display.Body files
                 [plotCmd p undefined ++ " " ++ commaConcat graphs],
              (n1, opts))

instance Graph.C graph => Display.C (T graph) where
   toScript plot =
      (Display.Script $
         State.Cons $ \(n, opts0) ->
            let defltOpts :: Graph.C graph => T graph -> OptionSet.T graph
                defltOpts _ = Graph.defltOptions
                opts1 = OptionSet.decons (defltOpts plot)
            in  (Display.Body [] $
                 OptionSet.diffToString opts0 opts1,
                 (n, opts1)))
      `mappend`
      toScript plot

plotCmd ::
   Graph.C graph =>
   T graph -> graph -> String
plotCmd _plot = Graph.command