module Graphics.BarChart.Parser where

import Text.CSV

import Data.List ( nub )
import Data.Maybe ( fromJust, fromMaybe )

import System.FilePath

import Graphics.BarChart.Types
import Graphics.BarChart.Rendering

-- | Converts a CSV file to be drawn as a chart where each bar may
--   consist of multiple blocks.
-- 
parseMultiBars :: Read a => [Label] -> CSV -> MultiBars a
parseMultiBars block_labels = MultiBars block_labels . map parseRecord
 where parseRecord (label:values) = (label,map read values)

-- | Converts a CSV file to be drawn as a chart where each bar has an
--   attached deviation depicted as an interval next to the bar.
-- 
parseIntervals :: Read a => CSV -> Intervals a
parseIntervals = Intervals . map parseRecord
 where parseRecord [label,m,l,u] = (label,(read m, read l, read u))

-- | Converts a CSV file to be drawn as a chart where each bar may
--   consist of multiple blocks which have an attached deviation
--   depicted as an interval next to them.
-- 
parseMultiBarIntervals :: Read a => [Label] -> CSV -> MultiBarIntervals a
parseMultiBarIntervals block_labels =
  MBIntervals block_labels . map parseRecord
 where parseRecord (label:fields) = (label, triples fields)
       triples (x:y:z:xs) = (read x,read y,read z) : triples xs
       triples _          = []

-- | Used by 'writeMultiBarChart' to create a 'BarChart' from a CSV file.
-- 
multiBarChart :: (Measurable a, Read a)
              => [Label]    -- ^ legend for blocks
              -> CSV        -- ^ comma separated values to depict
              -> BarChart a
multiBarChart block_labels = drawMultiBars . parseMultiBars block_labels

-- | Used by 'writeIntervalChart' to create a 'BarChart' from a CSV file.
-- 
intervalChart :: (Measurable a, Read a)
              => CSV        -- ^ comma separated values to depict 
              -> BarChart a
intervalChart = drawIntervals . parseIntervals

-- | Used by 'writeMultiBarIntervalChart' to create a 'BarChart' from
-- | a CSV file.
-- 
multiBarIntervalChart :: (Measurable a, Read a)
                      => [Label]    -- ^ legend for blocks
                      -> CSV        -- ^ comma separated values to depict
                      -> BarChart a
multiBarIntervalChart block_labels =
  drawMultiBarIntervals . parseMultiBarIntervals block_labels

-- | The first column of the CSV file is parsed as names of the
--   bars. The height of each bar corresponds to the sum of all
--   subsequent entries. If there is more than one entry, the bars are
--   split into blocks.
-- 
writeMultiBarChart :: Config   -- ^ where and how to draw the bar chart
                   -> FilePath -- ^ CSV file to read
                   -> [Label]  -- ^ if non-empty, used as legend for blocks
                   -> IO ()
writeMultiBarChart config file block_labels =
  do csv <- readCSV file
     let chart = multiBarChart block_labels csv :: BarChart Double
     renderWith config chart

-- | The first column of the CSV file is parsed as names of the
--   bars. Three entries following each bar name are parsed as mean,
--   minimum, and maximum value and depicted using an interval next to
--   the bar.
-- 
writeIntervalChart :: Config   -- ^ where and how to draw the bar chart
                   -> FilePath -- ^ CSV file to read
                   -> IO ()
writeIntervalChart config file =
  do csv <- readCSV file
     let chart = intervalChart csv :: BarChart Double
     renderWith config chart

-- | The first column of the CSV file is parsed as names of the
--   bars. The entries following each bar name are parsed as triples
--   of mean, minimum, and maximum value and depicted using an
--   interval next to the bar. The number of subsequent entries must
--   be a multiple of three and each bar is divided into a
--   corresponding number of blocks.
-- 
writeMultiBarIntervalChart :: Config   -- ^ where and how to draw the bar chart
                           -> FilePath -- ^ CSV file to read
                           -> [Label]  -- ^ legend for blocks
                           -> IO ()
writeMultiBarIntervalChart config file block_labels =
  do csv <- readCSV file
     let chart = multiBarIntervalChart block_labels csv :: BarChart Double
     renderWith config chart

readCSV :: FilePath -> IO CSV
readCSV file = either (fail . show) (return . clean) =<< parseCSVFromFile file
 where clean = filter (not . all null)