{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}

--
-- Based on the Elm VegaLite TimeTests.elm as of version 1.12.0
--

module TimeTests (testSpecs) where

import Data.Aeson (Value)
import Data.Aeson.QQ.Simple (aesonQQ)

import Graphics.Vega.VegaLite

import Prelude hiding (filter)

testSpecs :: [(String, VegaLite)]
testSpecs = [ ("timeYear", timeYear)
            , ("timeQuarter", timeQuarter)
            , ("timeQuarterMonth", timeQuarterMonth)
            , ("timeMonth", timeMonth)
            , ("timeMonthDate", timeMonthDate)
            , ("timeDate", timeDate)
            , ("timeYearMonthDateHours", timeYearMonthDateHours)
            , ("timeYearMonthDateHoursMinutes", timeYearMonthDateHoursMinutes)
            , ("timeYearMonthDateHoursMinutesSeconds", timeYearMonthDateHoursMinutesSeconds)
            , ("timeDay", timeDay)
            , ("timeHours", timeHours)
            , ("timeHoursMinutes", timeHoursMinutes)
            , ("timeHoursMinutesSeconds", timeHoursMinutesSeconds)
            , ("timeMinutes", timeMinutes)
            , ("timeMinutesSeconds", timeMinutesSeconds)
            , ("localTime", localTime)
            , ("utcTime", utcTime)
            , ("monthAggregate", monthAggregate)
            , ("withBar", withBar)
            , ("timeBand", timeBand)
            , ("withBarOrdinal", withBarOrdinal)
            , ("timeUnitTransform", timeUnitTransform)
            , ("parseAsUTC", parseAsUTC)
            , ("parseAsLocal", parseAsLocal)
            , ("parseAsFormat", parseAsFormat)
            , ("outputAsUTC", outputAsUTC)
            , ("outputScaledAsUTC", outputScaledAsUTC)
            , ("customizeStep", customizeStep)
            , ("nestedTime1", nestedTime1)
            , ("nestedTime2", nestedTime2)
            ]


timeByUnit :: BaseTimeUnit -> VegaLite
timeByUnit btu =
    let
        dataVals =
            dataFromUrl "https://gicentre.github.io/data/tests/timeTest.tsv" []

        enc =
            encoding
                . position X [ PName "date", PmType Temporal, PTimeUnit (TU btu) ]
                . position Y [ PName "temperature", PmType Quantitative
                             , PAggregate Mean, PScale [ SZero False ] ]
    in
    toVegaLite [ width 800, dataVals, enc []
               , mark Line [ MStrokeWidth 0.2 ] ]


timeYear :: VegaLite
timeYear = timeByUnit Year


timeQuarter :: VegaLite
timeQuarter = timeByUnit Quarter


timeQuarterMonth :: VegaLite
timeQuarterMonth = timeByUnit QuarterMonth


timeMonth :: VegaLite
timeMonth = timeByUnit Month


timeMonthDate :: VegaLite
timeMonthDate = timeByUnit MonthDate


timeDate :: VegaLite
timeDate = timeByUnit Date


timeYearMonthDateHours :: VegaLite
timeYearMonthDateHours = timeByUnit YearMonthDateHours


timeYearMonthDateHoursMinutes :: VegaLite
timeYearMonthDateHoursMinutes = timeByUnit YearMonthDateHoursMinutes


timeYearMonthDateHoursMinutesSeconds :: VegaLite
timeYearMonthDateHoursMinutesSeconds =
    timeByUnit YearMonthDateHoursMinutesSeconds


timeDay :: VegaLite
timeDay = timeByUnit Day


timeHours :: VegaLite
timeHours = timeByUnit Hours


timeHoursMinutes :: VegaLite
timeHoursMinutes = timeByUnit HoursMinutes


timeHoursMinutesSeconds :: VegaLite
timeHoursMinutesSeconds = timeByUnit HoursMinutesSeconds


timeMinutes :: VegaLite
timeMinutes = timeByUnit Minutes


timeMinutesSeconds :: VegaLite
timeMinutesSeconds = timeByUnit MinutesSeconds

-- TODO: Add milliseconds example
-- | SecondsMilliseconds
-- | Milliseconds


data Date
    = Local
    | UTC


parseTime :: Date -> VegaLite
parseTime dType =
    let
        format =
            case dType of
                Local ->
                    FoDate "%d %b %Y %H:%M"

                UTC ->
                    FoUtc "%d %b %Y %H:%M"

        tu =
            case dType of
                Local ->
                    PTimeUnit (TU YearMonthDateHours)

                UTC ->
                    PTimeUnit (Utc YearMonthDateHours)

        timeScale =
            case dType of
                Local ->
                    PScale [ SType ScTime ]

                UTC ->
                    PScale [ SType ScUtc ]

        dataVals =
            dataFromColumns [ Parse [ ( "date", format ) ] ]
                . dataColumn "date" (Strings [ "28 Oct 2017 22:00", "28 Oct 2017 23:00", "29 Oct 2017 00:00", "29 Oct 2017 01:00", "29 Oct 2017 02:00", "29 Oct 2017 03:00", "29 Oct 2017 04:00" ])
                . dataColumn "value" (Numbers [ 1, 2, 3, 4, 5, 6, 7 ])

        enc =
            encoding
                . position X [ PName "date", PmType Temporal
                             , tu, timeScale
                             , PAxis [ AxFormat "%d %b %H:%M" ] ]
                . position Y [ PName "value", PmType Quantitative ]
                . size [ MNumber 500 ]
    in
    toVegaLite [ width 800, dataVals [], enc [], mark Circle [] ]


localTime :: VegaLite
localTime = parseTime Local


utcTime :: VegaLite
utcTime = parseTime UTC


-- examples from the documentation
--
-- https://vega.github.io/vega-lite/docs/timeunit.html

seattleTemps :: Data
seattleTemps = dataFromUrl "https://vega.github.io/vega-lite/data/seattle-temps.csv" []

seattleWeather :: Data
seattleWeather = dataFromUrl "https://vega.github.io/vega-lite/data/seattle-weather.csv" []


monthAggregate :: VegaLite
monthAggregate =
  let enc = encoding
            . position X [ PName "date"
                         , PTimeUnit (TU Month)
                         , PmType Temporal
                         ]
            . position Y [ PName "temp"
                         , PAggregate Mean
                         , PmType Quantitative ]

  in toVegaLite
        [ seattleTemps
        , mark Line [MInterpolate Monotone]
        , enc []
        ]


withBar :: VegaLite
withBar =
  let enc = encoding
            . position X [ PName "date"
                         , PTimeUnit (TU Month)
                         , PmType Temporal
                         ]
            . position Y [ PName "precipitation"
                         , PAggregate Mean
                         , PmType Quantitative ]

  in toVegaLite
        [ seattleWeather
        , mark Bar []
        , enc []
        ]


-- https://vega.github.io/vega-lite/docs/timeunit.html#time-units-band

timeBand :: VegaLite
timeBand =
  let enc = encoding
            . position X [ PName "date", PTimeUnit (TU Month)
                         , PmType Temporal, PBand 0.5 ]
            . position Y [ PName "temp", PAggregate Mean
                         , PmType Quantitative ]

  in toVegaLite
        [ width 400
        , seattleTemps
        , enc []
        , mark Line [ MPoint (PMMarker [ MFill "black" ]) ]
        ]


-- https://vega.github.io/vega-lite/docs/timeunit.html#time-unit-with-ordinal-fields

withBarOrdinal :: VegaLite
withBarOrdinal =
  let enc = encoding
            . position X [ PName "date"
                         , PTimeUnit (TU Month)
                         , PmType Ordinal
                         ]
            . position Y [ PName "precipitation"
                         , PAggregate Mean
                         , PmType Quantitative ]

  in toVegaLite
        [ seattleWeather
        , mark Bar []
        , enc []
        ]


-- https://vega.github.io/vega-lite/docs/timeunit.html#transform

timeUnitTransform :: VegaLite
timeUnitTransform =
  let enc = encoding
            . position X [ PName "month"
                         , PmType Temporal
                         , PAxis [AxFormat "%b"]
                         ]
            . position Y [ PName "temp_max"
                         , PAggregate Max
                         , PmType Quantitative ]

  in toVegaLite
        [ seattleWeather
        , mark Line []
        , transform (timeUnitAs (TU Month) "date" "month" [])
        , enc []
        ]


-- https://vega.github.io/vega-lite/docs/timeunit.html#input

parseAsUTC :: VegaLite
parseAsUTC =
  toVegaLite [ dataFromColumns []
               . dataColumn "date" (Strings ["2011-10-10", "2011-10-12"])
               $ []
             , mark Point []
             , encoding
               . position Y [ PName "date"
                            , PmType Ordinal
                            , PTimeUnit (Utc Hours)
                            , PAxis [AxTitle "time"]
                            ]
               $ []
             ]


parseAsLocal :: VegaLite
parseAsLocal =
  toVegaLite [ dataFromColumns []
               . dataColumn "date" (Strings ["10 Oct 2011 22:48:00", "11 Oct 2022 23:00:00"])
               $ []
             , mark Point []
             , encoding
               . position Y [ PName "date"
                            , PmType Ordinal
                            , PTimeUnit (TU HoursMinutes)
                            , PAxis [AxTitle "time"]
                            ]
               $ []
             ]


parseAsFormat :: VegaLite
parseAsFormat =
  toVegaLite [ dataFromColumns [Parse [("date", FoUtc "%d %b %Y %H:%M:%S")]]
               . dataColumn "date" (Strings ["10 Oct 2011 22:48:00", "11 Oct 2022 23:00:00"])
               $ []
             , mark Point []
             , encoding
               . position Y [ PName "date"
                            , PmType Ordinal
                            , PTimeUnit (TU HoursMinutes)
                            , PAxis [AxTitle "time"]
                            ]
               $ []
             ]


-- https://vega.github.io/vega-lite/docs/timeunit.html#output

exampleData :: Data
exampleData =
  dataFromColumns []
  . dataColumn "date" (Strings [ "Sun, 01 Jan 2012 23:00:00"
                               , "Sun, 02 Jan 2012 00:00:00"
                               , "Sun, 02 Jan 2012 01:00:00"
                               , "Sun, 02 Jan 2012 02:00:00"
                               , "Sun, 02 Jan 2012 03:00:00"
                               ])

  . dataColumn "price" (Numbers [150, 100, 170, 165, 200])
  $ []


outputAsUTC :: VegaLite
outputAsUTC =
  toVegaLite [ exampleData
             , mark Line []
             , encoding
               . position X [ PName "date"
                            , PmType Temporal
                            , PTimeUnit (Utc YearMonthDateHoursMinutes)
                            , PAxis [AxLabelAngle 15]
                            ]
               . position Y [ PName "price"
                            , PmType Quantitative
                            ]
               $ []
             ]


outputScaledAsUTC :: VegaLite
outputScaledAsUTC =
  toVegaLite [ exampleData
             , mark Line []
             , encoding
               . position X [ PName "date"
                            , PmType Temporal
                            , PTimeUnit (TU YearMonthDateHoursMinutes)
                            , PScale [SType ScUtc]
                            , PAxis [AxLabelAngle 15]
                            ]
               . position Y [ PName "price"
                            , PmType Quantitative
                            ]
               $ []
             ]


-- https://vega.github.io/vega-lite/docs/timeunit.html#example-customizing-step

customizeStep :: VegaLite
customizeStep =
  toVegaLite [ dataFromColumns []
               . dataColumn "date" (Strings [ "Sun, 01 Jan 2012 00:00:00"
                                            , "Sun, 01 Jan 2012 00:01:00"
                                            , "Sun, 01 Jan 2012 00:02:00"
                                            , "Sun, 01 Jan 2012 00:03:00"
                                            , "Sun, 01 Jan 2012 00:04:00"
                                            , "Sun, 01 Jan 2012 00:05:00"
                                            , "Sun, 01 Jan 2012 00:06:00"
                                            , "Sun, 01 Jan 2012 00:07:00"
                                            , "Sun, 01 Jan 2012 00:08:00"
                                            , "Sun, 01 Jan 2012 00:09:00"
                                            , "Sun, 01 Jan 2012 00:10:00"
                                            , "Sun, 01 Jan 2012 00:11:00"
                                            , "Sun, 01 Jan 2012 00:12:00"
                                            , "Sun, 01 Jan 2012 00:13:00"
                                            , "Sun, 01 Jan 2012 00:14:00"
                                            , "Sun, 01 Jan 2012 00:15:00"
                                            ])
               . dataColumn "distance" (Numbers [ 1
                                                , 1
                                                , 2
                                                , 1
                                                , 4
                                                , 2
                                                , 5
                                                , 2
                                                , 6
                                                , 4
                                                , 1
                                                , 1
                                                , 3
                                                , 0
                                                , 2
                                                , 3
                                                ])
               $ []
             , mark Bar []
             , encoding
               . position X [ PName "date"
                            , PmType Temporal
                            , PTimeUnit (TUStep 5 Minutes)
                            ]
               . position Y [ PName "distance"
                            , PmType Quantitative
                            , PAggregate Sum
                            ]
               $ []
             ]


embeddedData, flattenedData :: Value
embeddedData = [aesonQQ|
[
   {"histo": {"date": "Sun, 02 Jan 2012 00:00:00", "price": 150}},
   {"histo": {"date": "Sun, 02 Jan 2012 00:00:00", "price": 100}},
   {"histo": {"date": "Sun, 02 Jan 2012 01:00:00", "price": 170}},
   {"histo": {"date": "Sun, 02 Jan 2012 02:00:00", "price": 165}},
   {"histo": {"date": "Sun, 02 Jan 2012 03:00:00", "price": 200}}
]
|]
  
flattenedData = [aesonQQ|
[
   {"date": "Sun, 02 Jan 2012 00:00:00", "price": 150},
   {"date": "Sun, 02 Jan 2012 00:00:00", "price": 100},
   {"date": "Sun, 02 Jan 2012 01:00:00", "price": 170},
   {"date": "Sun, 02 Jan 2012 02:00:00", "price": 165},
   {"date": "Sun, 02 Jan 2012 03:00:00", "price": 200}
]
|]
  

-- https://github.com/vega/vega-lite/issues/5662

nestedTime :: Value -> FieldName -> FieldName -> VegaLite
nestedTime jData xname yname =
  let desc = "Google's stock price over time."
      dvals = dataFromJson jData []
      
  in toVegaLite [ description desc
                , dvals
                , mark Line []
                , encoding
                  . position X [ PName xname
                               , PmType Temporal
                               , PTimeUnit (TU YearMonthDateHoursMinutes)
                               , PScale [SType ScUtc]
                               ]
                  . position Y [ PName yname
                               , PmType Quantitative
                               ]
                  $ []
                ]

  
nestedTime1, nestedTime2 :: VegaLite
nestedTime1 = nestedTime embeddedData "histo.date" "histo.price"
nestedTime2 = nestedTime flattenedData "date" "price"