{-# LANGUAGE OverloadedStrings #-}

--
-- Based on the Elm VegaLite AxisTests.elm in the 2.0 development version
--
module AxisTests (testSpecs) where

import qualified Data.Text as T

import Graphics.Vega.VegaLite

import Prelude hiding (filter)

testSpecs :: [(String, VegaLite)]
testSpecs = [ ("axis1", axis1)
            , ("axis1c", axis1c)
            , ("axis2", axis2)
            , ("axis2c", axis2c)
            , ("axis3", axis3)
            , ("axis3c", axis3c)
            , ("axis4", axis4)
            , ("axis4c", axis4c)
            , ("axis5", axis5)
            , ("axis5c", axis5c)
            , ("axis6", axis6)
            , ("axis6c", axis6c)
            , ("axis7", axis7)
            , ("axis7c", axis7c)
            , ("axis8", axis8)
            , ("axis8c", axis8c)
            , ("axisOverlapNone", axisOverlapNone)
            , ("axisOverlapParity", axisOverlapParity)
            , ("axisOverlapGreedy", axisOverlapGreedy)
            , ("zorder", zorder)
            , ("responsiveWidth", responsiveWidth)
            , ("responsiveHeight", responsiveHeight)
            , ("axisstyleempty", axisStyleEmpty)
            , ("axisstyleemptyx", axisStyleEmptyX)
            , ("axisstylex", axisStyleX)
            , ("axisstylexastyle", axisStyleXAStyle)
            , ("axisstylexy", axisStyleXY)
            , ("singleline", singleLine)
            , ("multiline", multiLine)
            ]


-- We do not provide these in hvega, so define them here to make copying
-- the Elm tests over easier.
--
pOrdinal, pQuant, pTemporal :: PositionChannel
pOrdinal = PmType Ordinal
pQuant = PmType Quantitative
pTemporal = PmType Temporal

pName :: T.Text -> PositionChannel
pName = PName


simpleData :: Data
simpleData =
  let xvals = map fromIntegral xs
      xs = [1::Int .. 100]
  in dataFromColumns []
       . dataColumn "x" (Numbers xvals)
       . dataColumn "catX" (Strings (map (T.pack . show) xs))
       . dataColumn "y" (Numbers xvals)
       $ []


temporalData ::  Data
temporalData =
  let dates = [ "2019-01-01 09:00:00"
              , "2019-01-02 09:00:00"
              , "2019-01-03 09:00:00"
              , "2019-01-04 09:00:00"
              , "2019-01-05 09:00:00"
              , "2019-01-06 09:00:00"
              , "2019-01-07 09:00:00"
              , "2019-01-08 09:00:00"
              , "2019-01-09 09:00:00"
              , "2019-01-10 09:00:00"
              , "2019-01-11 09:00:00"
              , "2019-01-12 09:00:00"
              ]

      xs = map fromIntegral [1::Int .. 12]

  in dataFromColumns []
     . dataColumn "date" (Strings dates)
     . dataColumn "y" (Numbers xs)
     $ []

xQuant, yQuant, catX, xDate :: [PositionChannel]
xQuant = [pName "x", pQuant]
yQuant = [pName "y", pQuant]
catX = [pName "catX", pOrdinal]
xDate = [pName "date", pTemporal]


axisBase :: Data -> [ConfigurationProperty] -> [PositionChannel] -> [PositionChannel] -> VegaLite
axisBase plotData confOpts xOpts yOpts =
  let enc = encoding . position X xOpts . position Y yOpts

      conf = case confOpts of
               [] -> []
               _ -> [configure (foldr configuration [] confOpts)]
      vs = conf ++ [ plotData, enc [], mark Line [ MPoint (PMMarker []) ] ]

  in toVegaLite vs

plotCfg :: [ConfigurationProperty]
plotCfg = [ AxisQuantitative AxXY [ DomainColor "orange"
                                  , GridColor "seagreen"
                                  , LabelFont "Comic Sans MS"
                                  , LabelOffset 10
                                  , TickOffset 10
                                  ]
          , AxisTemporal AxXY [ DomainColor "brown"
                              , DomainDash [4, 2]
                              , Grid False
                              , LabelColor "purple"
                              ]
          , PointStyle [ MStroke "black"
                       , MStrokeOpacity 0.4
                       , MStrokeWidth 1
                       , MFill "yellow"
                       ]
          , LineStyle [ MStroke "gray"
                      , MStrokeWidth 2
                      ]
          ]


axis1, axis1c, axis2, axis2c, axis3, axis3c, axis4, axis4c,
  axis5, axis5c, axis6, axis6c, axis7, axis7c, axis8, axis8c :: VegaLite
axis1 = axisBase simpleData [] xQuant yQuant
axis1c = axisBase simpleData plotCfg xQuant yQuant
axis2 = axisBase simpleData [] catX yQuant
axis2c = axisBase simpleData plotCfg catX yQuant
axis3 = axisBase simpleData [] xDate yQuant
axis3c = axisBase simpleData plotCfg xDate yQuant
axis4 =
  let x = PAxis [AxValues (Numbers [1, 25, 39, 90])] : xQuant
  in axisBase simpleData [] x yQuant
axis4c =
  let x = PAxis [AxValues (Numbers [1, 25, 39, 90])] : xQuant
  in axisBase simpleData plotCfg x yQuant
axis5 =
  let x = PAxis [AxValues (Strings ["1", "25", "39", "dummy", "90"])] : catX
  in axisBase simpleData [] x yQuant
axis5c =
  let x = PAxis [AxValues (Strings ["1", "25", "39", "dummy", "90"])] : catX
  in axisBase simpleData plotCfg x yQuant
axis6 =
  let x = PAxis [AxValues (DateTimes axDates)] : xDate

      axDates = [ [DTYear 2019, DTMonth Jan, DTDate 4 ]
                , [DTYear 2019, DTMonth Jan, DTDate 8 ]
                , [DTYear 2019, DTMonth Jan, DTDate 20 ]
                ]

  in axisBase temporalData [] x yQuant
axis6c =
  let x = PAxis [AxValues (DateTimes axDates)] : xDate

      axDates = [ [DTYear 2019, DTMonth Jan, DTDate 4 ]
                , [DTYear 2019, DTMonth Jan, DTDate 8 ]
                , [DTYear 2019, DTMonth Jan, DTDate 20 ]
                ]

  in axisBase temporalData plotCfg x yQuant
axis7 =
  let x = PAxis [AxLabelExpr "datum.value / 100"] : xQuant
  in axisBase simpleData [] x yQuant
axis7c =
  let x = PAxis [AxLabelExpr "datum.value / 100"] : xQuant
  in axisBase simpleData plotCfg x yQuant
axis8 =
  let x = PAxis [AxLabelExpr "'number' + datum.label"] : catX
  in axisBase simpleData [] x yQuant
axis8c =
  let x = PAxis [AxLabelExpr "'number' + datum.label"] : catX
  in axisBase simpleData plotCfg x yQuant


overlap :: OverlapStrategy -> VegaLite
overlap strat =
  let dvals = dataFromColumns []
              . dataColumn "x" (Numbers [ 0.1, 0.11, 0.2, 0.21, 0.5 ])
              . dataColumn "y" (Numbers [ 100, 101, 102, 103, 101 ])

      axisOpts = PAxis [ AxLabelOverlap strat
                       , AxLabelFontSize 20
                       ]

      enc = encoding
            . position X [ PName "x", PmType Quantitative, axisOpts ]
            . position Y [ PName "y", PmType Quantitative, axisOpts ]

  in toVegaLite [ dvals [], enc [], mark Circle [] ]


axisOverlapNone :: VegaLite
axisOverlapNone = overlap ONone

axisOverlapParity :: VegaLite
axisOverlapParity = overlap OParity

axisOverlapGreedy :: VegaLite
axisOverlapGreedy = overlap OGreedy


-- From
-- https://github.com/gicentre/elm-vegalite/issues/15#issuecomment-524527125
--
zorder :: VegaLite
zorder =
  let dcols = dataFromColumns []
              . dataColumn "x" (Numbers [ 20, 10 ])
              . dataColumn "y" (Numbers [ 10, 20 ])
              . dataColumn "cat" (Strings [ "a", "b" ])

      axis lbl z = [ PName lbl, PmType Quantitative, PAxis [ AxZIndex z ] ]
      enc = encoding
            . position X (axis "x" 2)
            . position Y (axis "y" 1)
            . color [ MName "cat", MmType Nominal, MLegend [] ]

      cfg = configure
            . configuration (Axis [ GridWidth 8 ])
            . configuration (AxisX [ GridColor "red" ])
            . configuration (AxisY [ GridColor "blue" ])

  in toVegaLite [ cfg []
                , dcols []
                , enc []
                , mark Circle [ MSize 5000, MOpacity 1 ]
                ]


responsive :: PropertySpec -> VegaLite
responsive prop =
  let enc = encoding
            . position X [PName "x", PmType Quantitative]
            . position Y [PName "y", PmType Quantitative]

  in toVegaLite [ prop, simpleData, enc []
                , mark Line [MPoint (PMMarker [])] ]

responsiveWidth, responsiveHeight :: VegaLite
responsiveWidth = responsive widthOfContainer
responsiveHeight = responsive heightOfContainer


carData :: Data
carData = dataFromUrl "https://vega.github.io/vega-lite/data/cars.json" []


carEnc :: [AxisProperty] -> [AxisProperty] -> PropertySpec
carEnc xOpts yOpts =
  let toAxis n l o = position n ([ PName l, PmType Quantitative ]
                                 ++ if null o
                                    then []
                                    else [PAxis o])

  in encoding
     . toAxis X "Horsepower" xOpts
     . toAxis Y "Miles_per_Gallon" yOpts
     . color [ MName "Origin", MmType Nominal, MLegend [] ]
     $ []

axisStyleEmpty :: VegaLite
axisStyleEmpty =
  let cfg = configure
            . configuration (AxisNamedStyles [])

  in toVegaLite [ cfg []
                , carData
                , carEnc [] []
                , mark Point []
                ]

axisStyleEmptyX :: VegaLite
axisStyleEmptyX =
  let cfg = configure
            . configuration (AxisNamedStyles [("x-style", [])])

  in toVegaLite [ cfg []
                , carData
                , carEnc [AxStyle ["x-style"]] []
                , mark Point []
                ]


axisStyleX :: VegaLite
axisStyleX =
  let cfg = configure
            . configuration (AxisNamedStyles [("x-style", [ AxDomainColor "orange"
                                                          , AxGridColor "lightgreen"
                                                          , AxLabelExpr xexpr ])])

      xexpr = "if (datum.value <= 100, 'low:' + datum.label, 'high:' + datum.label)"

  in toVegaLite [ cfg []
                , carData
                , carEnc [AxStyle ["x-style"]] []
                , mark Point []
                ]


-- check AStyle; should give same look as axisStyleX
axisStyleXAStyle :: VegaLite
axisStyleXAStyle =
  let cfg = configure
            . configuration (AxisNamedStyles [("x-style", [ AxDomainColor "orange"
                                                          , AxGridColor "lightgreen"
                                                          , AxLabelExpr xexpr ])])
            . configuration (AxisX [AStyle ["x-style"]])

      xexpr = "if (datum.value <= 100, 'low:' + datum.label, 'high:' + datum.label)"

  in toVegaLite [ cfg []
                , carData
                , carEnc [] []
                , mark Point []
                ]


axisStyleXY :: VegaLite
axisStyleXY =
  let cfg = configure
            . configuration (AxisNamedStyles [ ("x-style", [ AxDomainColor "orange"
                                                           , AxGridColor "lightgreen"
                                                           , AxLabelExpr xexpr ])
                                             , ("y-style", [ AxDomain False
                                                           , AxGrid False
                                                           , AxLabels False
                                                           , AxTicks False
                                                           , AxNoTitle ])
                                             ])

      xexpr = "if (datum.value <= 100, 'low:' + datum.label, 'high:' + datum.label)"

  in toVegaLite [ cfg []
                , carData
                , carEnc [AxStyle ["x-style"]] [AxStyle ["y-style"]]
                , mark Point []
                ]


singleLine :: VegaLite
singleLine =
  let xOpts = [ AxLabelExpr "datum.label + ' horses'" ]
      yOpts = [ AxLabelExpr "datum.label+' mpg'" ]

  in toVegaLite [ carData
                , carEnc xOpts yOpts
                , mark Point []
                ]


multiLine :: VegaLite
multiLine =
  let xOpts = [ AxLabelExpr "datum.label + ' horses'"
              , AxLabelLineHeight 22
              , AxLabelFontSize 11
              ]
      yOpts = [ AxLabelExpr "datum.label+' mpg'"
              , AxLabelFontSize 22 ]

  in toVegaLite [ carData
                , carEnc xOpts yOpts
                , mark Point []
                , configure
                  . configuration (LineBreakStyle " ")
                  . configuration (Axis [LabelLineHeight 20])
                  $ []
                ]