{-# LANGUAGE OverloadedStrings #-}

module Hasmin.Types.StylesheetSpec where

import Data.Text (Text, unpack)

import Hasmin.Parser.Internal
import Hasmin.TestUtils
import Hasmin.Class
import Hasmin.Types.Stylesheet
import Hasmin

combineAdjacentMediaQueriesTests :: Spec
combineAdjacentMediaQueriesTests =
     describe "Combines adjacent @media rules" $
      mapM_ f combineAdjacentMediaQueriesTestsInfo
  where f (t1, t2) = it (unpack t1) $ minifyCSS t1 `parseSatisfies` (== t2)

combineAdjacentMediaQueriesTestsInfo :: [(Text, Text)]
combineAdjacentMediaQueriesTestsInfo =
  [("@media (min-width:24rem){.Fz\\(s2\\)\\@xs{font-size:1.2rem;}}@media (min-width:24rem){.Px\\(s04\\)\\@xs{padding-left:.25rem; padding-right:.25rem;}}",
    "@media (min-width:24rem){.Fz\\(s2\\)\\@xs{font-size:1.2rem}.Px\\(s04\\)\\@xs{padding-left:.25rem;padding-right:.25rem}}")


  {- TODO make Eq instance handle this equality.
   -
  ,("@media all and (min-width:24rem){.Fz\\(s2\\)\\@xs{font-size:1.2rem;}}@media (min-width:24rem){.Px\\(s04\\)\\@xs{padding-left:.25rem; padding-right:.25rem;}}",
    "@media (min-width:24rem){.Fz\\(s2\\)\\@xs{font-size:1.2rem}.Px\\(s04\\)\\@xs{padding-left:.25rem;padding-right:.25rem}}")
  -}
  ]

atRuleTests :: Spec
atRuleTests = do
    describe "at rules parsing and printing" $
      mapM_ (matchSpec atRule) atRuleTestsInfo
    describe "@supports minification" $
      mapM_ (matchSpec (minifyWithTestConfig <$> atRule)) atSupportsTestInfo
    describe "@import minification" $
      mapM_ (matchSpec (minifyWithTestConfig <$> atRule)) atImportTestInfo
    describe "@media minification" $
      mapM_ (matchSpec (minifyWithTestConfig <$> atRule)) atMediaTestInfo

atMediaTestInfo :: [(Text, Text)]
atMediaTestInfo =
  [("@media all {h1{color:red}}",
    "@media {h1{color:red}}")
  ,("@media all and (min-width: 500px){h1 {color: red}}",
    "@media (min-width:500px){h1{color:red}}")
  ,("@media not all{h1{color:red}}",
    "@media not all{h1{color:red}}")
  ,("@media all and (min-width:500px), not all and (min-device-pixel-ratio:0){h1{a:a}}",
    "@media (min-width:500px),not all and (min-device-pixel-ratio:0){h1{a:a}}")
  ,("@media (min-width:500px),all and (min-device-pixel-ratio:0){h1{a:a}}",
    "@media (min-width:500px),(min-device-pixel-ratio:0){h1{a:a}}")
  ,("@media only all{h1{a:a}}",
    "@media only all{h1{a:a}}")
  ]

atImportTestInfo :: [(Text, Text)]
atImportTestInfo =
  [("@import  url(\'landscape.css\');",
      "@import \"landscape.css\";")
  ,("@import url(landscape.css);",
      "@import \"landscape.css\";")
  ,("@import url(landscape.css) all;",
      "@import \"landscape.css\";")
  ,("@import url(landscape.css) screen, all and (min-width:500px);",
      "@import \"landscape.css\" screen,(min-width:500px);")
  ,("@import url(landscape.css) all and (min-width:500px);",
      "@import \"landscape.css\" (min-width:500px);")
  ]

mergeRulesTest :: Spec
mergeRulesTest =
    describe "Rules merging" $
      mapM_ (matchSpecWithDesc (f . minifyWithTestConfig <$> rules)) mergeRulesTestsInfo
  where f = mconcat . map toText

mergeRulesTestsInfo :: [(String, Text, Text)]
mergeRulesTestsInfo =
  [("Combine adjacent rules with the same declarations",
      "h1{margin:10px}h2{margin:10px}",
      "h1,h2{margin:10px}")
  ,("Merge rules with identical selectors, and combine margin-bottom into margin",
    ".a{margin:10px}.a{margin-bottom:5px}",
    ".a{margin:10px 10px 5px}")
  ,("Merge rules with identical selectors, and remove overwritten margin-bottom",
    ".a{margin-bottom:10px}.a{margin:5px}",
    ".a{margin:5px}")
  ,("Merge rules with identical declarations, when a same specificity rule is in-between but the declarations don't interfere",
    ".a{border-left-color:red}.b{border-right-color:blue}.c{border-left-color:red}",
    ".a,.c{border-left-color:red}.b{border-right-color:blue}")
  ,("Don't merge rules that share selectors, but not every selector",
    "table,video{margin:0}ol,ul{list-style:none}table{border-collapse:collapse}",
    "table,video{margin:0}ol,ul{list-style:none}table{border-collapse:collapse}")
  ,("Don't combine rules with the same selectors when there is another in-between with the same specificity and a declaration that clashes",
    ".a p{margin:10px 0}.b p{margin:10px auto}.a p{margin-bottom:5px}",
    ".a p{margin:10px 0}.b p{margin:10px auto}.a p{margin-bottom:5px}")
  ,("Merge example from csso/issues/217 properly",
    ".a{float:left}.b{background:red}.c{color:#fff}.d{text-decoration:none}.e{float:left}.d{float:left}",
    ".a,.e,.d{float:left}.b{background:red}.c{color:#fff}.d{text-decoration:none}")
  ,("When merging rules, ignores @keyframes rule inbetween",
    ".a{color:red}@keyframes foo{0%{frame:1}to{frame:2}}.b{color:red}",
    ".a,.b{color:red}@keyframes foo{0%{frame:1}to{frame:2}}")
  ,("When merging rules, ignores @font-face rule inbetween",
    ".a{color:red}@font-face{test:1}.b{color:red}",
    ".a,.b{color:red}@font-face{test:1}")
    {- TODO
  ,("Merge rules with identical selectors, and combine font-weight into font",
    ".a{font:700 65%/1.5 sans-serif}.a{font-weight:400}",
    ".a{font:400 65%/1.5 sans-serif}")
    -}
  {-TODO considering gzip, is implementing this worth it?
  ,("csso 1",
    "a{padding:0;margin:0}b{padding:0}",
    "a,b{padding:0}a{margin:0}")
  ,("csso 2",
    "a{padding:0}b{padding:0;margin:0}",
    "a,b{padding:0}b{margin:0}")
  ,("csso 3",
    "a{padding:0}b{padding:0;margin:0}c{margin:0}",
    "a,b{padding:0}b,c{margin:0}")
  ,("csso 4",
    "a{padding:0}elementWithAnAbsurdlyVeryLongName{padding:0;margin:0}c{margin:0}",
    "a{padding:v}elementWithAnAbsurdlyVeryLongName{padding:0;margin:0}c{margin:0}")
  ,("csso ",
    ".foo{margin:0;padding:2px}.bar{margin:1px;padding:2px}",
    ".bar,.foo{margin:0;padding:2px}.bar{margin:1px}")
  -}
  ]

atSupportsTestInfo :: [(Text, Text)]
atSupportsTestInfo =
  [("@supports not (not (a:a)){s{b:b}}",
      "@supports (a:a){s{b:b}}")
  ]

-- TODO test for
{-
.gallery-loading .gallery-loading-container {
  background: #fff url("//corporate-website-test.s3.amazonaws.com/wp-content/themes/ggs-rcw/img/spinner_white_125px.gif") no-repeat center center;
}
-}
atRuleTestsInfo :: [(Text, Text)]
atRuleTestsInfo =
  [("@charset \"UTF-8\";",
      "@charset \"UTF-8\";")
  ,("@import/**/ 'custom.css' ;",
      "@import 'custom.css';")
  ,("@import  \"common.css\" screen , projection;",
      "@import \"common.css\" screen,projection;")
  ,("@import  url(\'landscape.css\')  screen  and  (orientation: landscape);",
      "@import url(\'landscape.css\') screen and (orientation:landscape);")
  ,("@namespace /**/ prefix url(XML-namespace-URL);",
      "@namespace prefix url(XML-namespace-URL);")
  ,("@namespace  prefix /**/  \"XML-namespace-URL\";",
      "@namespace prefix \"XML-namespace-URL\";")
  ,("@media screen {s{a:a}}",
      "@media screen{s{a:a}}")
  ,("@media screen and (min-width: 768px){s{a:a}}",
      "@media screen and (min-width:768px){s{a:a}}")
  ,("@keyframes p { from { background-position: 40px 0 } to { background-position: 0 0 } }",
      "@keyframes p{from{background-position:40px 0}to{background-position:0 0}}")
  ,("@font-face  /**/ {a:a;}",
      "@font-face{a:a}")
  -- Uncomment once custom properties are supported
  -- ,("@supports (--foo: green) { body { color: green; } }",
     -- "@supports (--foo:green){body{color:green}}")
  ,("@supports ( transform-style: preserve ) or ( -moz-transform-style: preserve ){s{a:a}}",
     "@supports (transform-style:preserve) or (-moz-transform-style:preserve){s{a:a}}")
  ,("@supports ( display : table-cell ) and ( not ( display : list-item ) ){s{a:a}}",
    "@supports (display:table-cell) and (not (display:list-item)){s{a:a}}")
  ,("@supports not ( (a:a)  and  (b:b) ) {s{a:a}}",
    "@supports not ((a:a) and (b:b)){s{a:a}}")
  ,("@supports ((yoyo: yaya) or (margin: 0) or (answer: 42)) { div { background-color:green; } }",
    "@supports ((yoyo:yaya) or (margin:0) or (answer:42)){div{background-color:green}}")
  ,("@supports (margin: 0) {@media  not  all { div { background-color:red; }}}",
    "@supports (margin:0){@media not all{div{background-color:red}}}")
  -- ,("@document url(http://www.w3.org/) , url-prefix(http://www.w3.org/Style/),\
  --             \ domain(mozilla.org), regexp(\"https:.*\")"
  -- ,("@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/),\
    --           \domain(mozilla.org),regexp(\"https:.*\")"
  -- ,("@page { margin: 1in }",
  --   "@page{margin:1in}")
  -- ,("@page :left { font-size: 20pt; }",
    -- "@page:left{font-size:20pt}")
  -- ,("@page toc, index { size:8.5in 11in; }",
    -- "@page toc,index{size:8.5in 11in}")
  -- ,("@viewport { min-width: 640px; max-width: 800px; }",
    -- "@viewport{min-width:640px;max-width:800px}")
  -- ,("@counter-style circled-alpha { system: fixed; symbols: Ⓐ Ⓑ Ⓒ; suffix: " "; }",
    -- "@counter-style circled-alpha{system:fixed;symbols:Ⓐ Ⓑ Ⓒ;suffix:" "}")
  -- ,("@font-feature-values Jupiter Sans { @swash { delicate: 1; flowing: 2; } }",
    -- "@font-feature-values Jupiter Sans{@swash{delicate:1;flowing:2}}",
  ]

collapseLonghandTests :: Spec
collapseLonghandTests =
    describe "TRBL Longhand collapsing" $
      mapM_ (matchSpec f) collapseLonghandsTestsInfo
  where f = Declarations . collapse <$> declarations

collapseLonghandsTestsInfo :: [(Text, Text)]
collapseLonghandsTestsInfo =
  [("margin-top:1px;margin-right:2px;margin-bottom:3px;margin-left:4px",
    "margin:1px 2px 3px 4px")
  ,("margin-left:4px;margin-bottom:3px;margin-right:2px;margin-top:1px;",
    "margin:1px 2px 3px 4px")
  ,("margin-left:4px;margin-bottom:3px;padding-top:0;margin-right:2px;margin-top:1px;",
    "padding-top:0;margin:1px 2px 3px 4px")
  ,("margin-bottom:3px;padding-top:0;margin-right:2px;margin-top:1px;",
    "margin-bottom:3px;padding-top:0;margin-right:2px;margin-top:1px")
  ,("padding-left:4px;padding-bottom:3px;padding-right:2px;padding-top:1px;",
    "padding:1px 2px 3px 4px")
  ,("border-left-color:#004;border-bottom-color:#003;border-right-color:#002;border-top-color:#001;",
    "border-color:#001 #002 #003 #004")
  ]


spec :: Spec
spec = do atRuleTests
          combineAdjacentMediaQueriesTests
          collapseLonghandTests
          mergeRulesTest

main :: IO ()
main = hspec spec
