
module Paper.Graph(graphLog, graphCreate) where

import Data.List
import qualified Data.Map as Map
import Data.Ord
import System.Time
import System.Process.Extra

import Graphics.Google.Chart


graphLog :: FilePath -> [(String,Int)] -> IO ()
graphLog dest xs = do
    t <- getClockTime
    t <- toCalendarTime t
    appendFile dest $ unlines [show (t,a,b) | (a,b) <- xs]



-- load the data
graphLoad :: FilePath -> [String] -> IO [(Date,String,Int)]
graphLoad src files = do
    src <- readFile src
    res <- return [(date a,b,c) | s <- lines src, not $ null s, let (a,b,c) = read s, b `elem` files]
    return $ sortBy (comparing fst3) res

fst3 (x,_,_) = x

-- year, month (1 based), day (1 based)
type Date = (Int,Int,Int)

date :: CalendarTime -> Date
date t = (ctYear t, fromEnum (ctMonth t) + 1, ctDay t)


incDate :: Date -> Date
incDate (a,12,31) = (a+1,1,1)
incDate (a, b,31) = (a,b+1,1)
incDate (a, b, c) = (a,b,c+1)


totalCounts :: [FilePath] -> [(Date,FilePath,Int)] -> [(Date,Int)]
totalCounts _ [] = [((2000,1,1),0)]
totalCounts files xs = f (Map.fromList (zip files $ repeat 0)) xs steps
    where
        steps = iterate incDate $ fst3 (head xs)

        f now xxs@((a,b,c):xs) (s:ss)
            | a > s = dump s now : f now xxs ss
            | otherwise = f (Map.insert b c now) xs (s:ss)
        f now [] (s:ss) = [dump s now]

        dump s mp = (s, sum $ Map.elems mp)


graphCreate :: FilePath -> FilePath -> [String] -> IO ()
graphCreate src dest files = do
    xs <- graphLoad src files
    let url = graphUrl $ totalCounts files xs
    system $ "wget \"" ++ url ++ "\" -O \"" ++ dest ++ "\""
    return ()


-- a list of date/count pairs
-- all the dates must be in order
graphUrl :: [(Date,Int)] -> String
graphUrl dat = chartURL $
        setSize 400 300 $
        setTitle "Word Count" $
        setAxisTypes [AxisBottom,AxisLeft] $
        setAxisLabels [xAxis, yAxis] $
        setData (encodeDataSimple [map scale allY]) $
        newLineChart
    where
        -- calculate the bounding box
        minX = let (a,b,_) = fst (head dat) in (a,b,1)
        maxX = let (a,b,_) = fst (last dat) in incDate (a,b,31)
        allY = if length dat == 1 then replicate 2 $ snd $ head dat else map snd dat
        minY = (minimum allY `div` 1000) * 1000
        maxY = (maximum allY `div` 1000) * 1000 + 1000

        -- calculate the axis
        yAxis = map show [minY, minY+1000 .. maxY]
        xAxis = map g $ takeWhile (<= maxX) $ iterate f minX
            where
                f (a,b,c) = incDate (a,b,31)
                g (a,b,c) = take 3 (show (toEnum (b-1) :: Month)) ++ " " ++ show (a `mod` 100)

        -- scaling (new range is 0 <= x <= 61)
        scale y = (61 * (y - minY)) `div` (maxY - minY)
