{-# LANGUAGE
    EmptyDataDecls
  , OverloadedStrings
  , GeneralizedNewtypeDeriving
  , FlexibleInstances
  #-}
module Clay.Size
(

-- * Size type.
  Size
, Abs
, Rel

-- * Size constructors.

, px
, pt
, em
, ex
, pct

-- * Shorthands for mutli size-valued properties.

, sym
, sym2
, sym3

-- * Angle type.

, Angle
, Deg
, Rad

-- * Constructing angles.

, deg
, rad

)
where

import Data.Monoid
import Data.Text (pack)

import Clay.Common
import Clay.Property
import Clay.Stylesheet

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

-- | Sizes can be relative like percentages.
data Rel

-- | Sizes can be absolute like pixels, points, etc.
data Abs

newtype Size a = Size Value
  deriving (Val, Auto, Normal, Inherit, None, Other)

-- | Size in pixels.

px :: Integer -> Size Abs
px i = Size (value (pack (show i) <> "px"))

-- | Size in points.

pt :: Double -> Size Abs
pt i = Size (value (pack (show i) <> "pt"))

-- | Size in em's.

em :: Double -> Size Abs
em i = Size (value (pack (show i) <> "em"))

-- | Size in ex'es.

ex :: Double -> Size Abs
ex i = Size (value (pack (show i) <> "ex"))

-- | Size in percentages.

pct :: Double -> Size Rel
pct i = Size (value (pack (show i) <> "%"))

instance Num (Size Abs) where
  fromInteger = px
  (+)    = error   "plus not implemented for Size"
  (*)    = error  "times not implemented for Size"
  abs    = error    "abs not implemented for Size"
  signum = error "signum not implemented for Size"

instance Fractional (Size Abs) where
  fromRational = em . fromRational

instance Num (Size Rel) where
  fromInteger = pct . fromInteger
  (+)    = error   "plus not implemented for Size"
  (*)    = error  "times not implemented for Size"
  abs    = error    "abs not implemented for Size"
  signum = error "signum not implemented for Size"

instance Fractional (Size Rel) where
  fromRational = pct . fromRational

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

sym :: (Size a -> Size a -> Size a -> Size a -> Css) -> Size a -> Css
sym k a = k a a a a

sym3 :: (Size a -> Size a -> Size a -> Size a -> Css) -> Size a -> Size a -> Size a -> Css
sym3 k tb l r = k tb l tb r

sym2 :: (Size a -> Size a -> Size a -> Size a -> Css) -> Size a -> Size a -> Css
sym2 k tb lr = k tb lr tb lr

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

data Deg
data Rad

newtype Angle a = Angle Value
  deriving (Val, Auto, Inherit, Other)

-- | Angle in degrees.

deg :: Double -> Angle Deg
deg i = Angle (value (pack (show i) <> "deg"))

-- | Angle in radians.

rad :: Double -> Angle Rad
rad i = Angle (value (pack (show i) <> "rad"))

instance Num (Angle Deg) where
  fromInteger = deg . fromInteger
  (+)    = error   "plus not implemented for Angle"
  (*)    = error  "times not implemented for Angle"
  abs    = error    "abs not implemented for Angle"
  signum = error "signum not implemented for Angle"

instance Fractional (Angle Deg) where
  fromRational = deg . fromRational

instance Num (Angle Rad) where
  fromInteger = rad . fromInteger
  (+)    = error   "plus not implemented for Angle"
  (*)    = error  "times not implemented for Angle"
  abs    = error    "abs not implemented for Angle"
  signum = error "signum not implemented for Angle"

instance Fractional (Angle Rad) where
  fromRational = rad . fromRational