{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-}
module Clay.Box
( BoxType
, paddingBox, borderBox, contentBox
, boxSizing
-- * @box-shadow@
-- $box-shadow
, boxShadow
, shadow
, shadowWithBlur
, shadowWithSpread
, bsInset
, bsColor
-- ** Deprecated
-- $box-shadow-deprecated
, boxShadow'
, boxShadowWithSpread
, boxShadows
, insetBoxShadow
)
where

import Data.Monoid
import Data.List.NonEmpty (NonEmpty)

import Clay.Color
import Clay.Common
import Clay.Property
import Clay.Stylesheet
import Clay.Size
import Clay.Border

-------------------------------------------------------------------------------

newtype BoxType = BoxType Value
  deriving (Val, Inherit)

paddingBox, borderBox, contentBox :: BoxType

paddingBox = BoxType "padding-box"
borderBox  = BoxType "border-box"
contentBox = BoxType "content-box"

-------------------------------------------------------------------------------

boxSizing :: BoxType -> Css
boxSizing = prefixed (browsers <> "box-sizing")

-------------------------------------------------------------------------------

-- $box-shadow
--
-- === Formal argument syntax
--
-- > none | <shadow>#
-- > where 
-- > <shadow> = inset? && <length>{2,4} && <color>?
-- > 
-- > where 
-- > <color> = <rgb()> | <rgba()> | <hsl()> | <hsla()> | <hex-color> | <named-color> | currentcolor | <deprecated-system-color>
-- > 
-- > where 
-- > <rgb()> = rgb( [ [ <percentage>{3} | <number>{3} ] [ / <alpha-value> ]? ] | [ [ <percentage>#{3} | <number>#{3} ] , <alpha-value>? ] )
-- > <rgba()> = rgba( [ [ <percentage>{3} | <number>{3} ] [ / <alpha-value> ]? ] | [ [ <percentage>#{3} | <number>#{3} ] , <alpha-value>? ] )
-- > <hsl()> = hsl( [ <hue> <percentage> <percentage> [ / <alpha-value> ]? ] | [ <hue>, <percentage>, <percentage>, <alpha-value>? ] )
-- > <hsla()> = hsla( [ <hue> <percentage> <percentage> [ / <alpha-value> ]? ] | [ <hue>, <percentage>, <percentage>, <alpha-value>? ] )
-- > 
-- > where 
-- > <alpha-value> = <number> | <percentage>
-- > <hue> = <number> | <angle>

newtype BoxShadow = BoxShadow Value
  deriving (Val, Inherit, Initial, Unset, None, Other)

-- | This function will usually take a singleton list, but requiring a (non-empty)
-- list prevents accidentally applying the modifiers ('bsInset', 'bsColor')
-- incorrectly.
--
-- 'pure' (from "Control.Applicative") creates a singleton list.
--
-- > boxShadow . pure $ none
-- > boxShadow . pure $ shadow (px 1) (px 1)
--
-- Use with @{-# LANGUAGE OverloadedLists #-}@ for the simplest list syntax.
--
-- > boxShadow [none]
-- > boxShadow [shadow (px 1) (px 1)]
--
-- This is recommended for supplying multiple 'BoxShadow' values.
--
-- > boxShadow [shadowWithBlur (em 2) (em 1), bsInset . bsColor red $ shadow (px 1) (px 2)]
boxShadow :: NonEmpty BoxShadow -> Css
boxShadow = prefixed (browsers <> "box-shadow") . value

shadow
  :: Size a -- ^ Horizontal offset
  -> Size a -- ^ Vertical offset
  -> BoxShadow
shadow x y = BoxShadow . value $ (x ! y)

shadowWithBlur
  :: Size a -- ^ Horizontal offset
  -> Size a -- ^ Vertical offset
  -> Size a -- ^ Blur radius
  -> BoxShadow
shadowWithBlur x y w = BoxShadow . value $ (x ! y ! w)

-- | While this function is the correct type to work with the 'sym', 'sym2'
-- or 'sym3' functions, such applications are unlikely to be meaningful.
shadowWithSpread
  :: Size a -- ^ Horizontal offset
  -> Size a -- ^ Vertical offset
  -> Size a -- ^ Blur radius
  -> Size a -- ^ Spread radius
  -> BoxShadow
shadowWithSpread x y blurRadius spreadRadius =
    BoxShadow . value $ (x ! y ! blurRadius ! spreadRadius)

-- | Adapt the provided @box-shadow@ with the @inset@ prefix.
-- 
-- > boxShadow . pure . bsInset
bsInset :: BoxShadow -> BoxShadow
bsInset (BoxShadow v) = BoxShadow . value $ ("inset" :: Value, v)

-- | Supply a color to the provided @box-shadow@.
bsColor :: Color -> BoxShadow -> BoxShadow
bsColor c (BoxShadow v) = BoxShadow . value $ (v, c)
infixr 9 `bsColor`

-------------------------------------------------------------------------------

-- $box-shadow-deprecated
--
-- The old implementation was both restrictive and slightly off-spec. It
-- shall be discontinued in a future release. The 'boxShadow' name has already
-- been taken, so the functionality that used to provide is now provided by
-- 'boxShadow\''.

-- | This is the drop-in replacement for the old 'boxShadow' function (< 0.13).
-- It is possible to continue for now
boxShadow' :: Size a -> Size a -> Size a -> Color -> Css
boxShadow' x y w c = prefixed (browsers <> "box-shadow") (x ! y ! w ! c)
{-# DEPRECATED boxShadow' "This function is only present for compatibility purposes and will be removed." #-}

-- | Replace calls to this function as follows (using -XOverloadedLists)
--
-- > boxShadowWithSpread x y rb rs c
--
-- > boxShadow [c `bsColor` shadowWithSpread x y rb rs]
boxShadowWithSpread :: Size a -> Size a -> Size a -> Size a -> Color -> Css
boxShadowWithSpread x y blurRadius spreadRadius color =
    prefixed (browsers <> "box-shadow") (x ! y ! blurRadius ! spreadRadius ! color)
{-# DEPRECATED boxShadowWithSpread "This function has been replaced with shadowWithSpread and bsColor and will be removed." #-}

-- | Replace calls to this function as follows (using -XOverloadedLists)
--
-- > boxShadows [(x1, y1, rb1, c1), (x2, y2, rb2, c2)]
--
-- > boxShadow
-- >   [ c1 `bsColor` shadowWithBlur x1 y1 rb1
-- >   , c2 `bsColor` shadowWithBlur x2 y2 rb2
-- >   ]
boxShadows :: [(Size a, Size a, Size a, Color)] -> Css
boxShadows = prefixed (browsers <> "box-shadow") . map (\(a, b, c, d) -> a ! b ! c ! d)
{-# DEPRECATED boxShadows "This function is replaced with boxShadow and will be removed." #-}

-- | Replace calls to this function as follows (using -XOverloadedLists)
--
-- > insetBoxShadow s x y rb c
--
-- > boxShadow [bsInset $ c `bsColor` shadowWithBlur x y rb]
insetBoxShadow :: Stroke -> Size a -> Size a -> Size a -> Color -> Css
insetBoxShadow x y w c z = prefixed (browsers <> "box-shadow") (x ! y ! w ! c ! z)
{-# DEPRECATED insetBoxShadow "This function has been replaced with shadowWithSpread, bsInset and bsColor and will be removed." #-}