{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}
-- |
-- Module      : Graphics.Color.Model.HSL
-- Copyright   : (c) Alexey Kuleshevich 2018-2020
-- License     : BSD3
-- Maintainer  : Alexey Kuleshevich <lehins@yandex.ru>
-- Stability   : experimental
-- Portability : non-portable
--
module Graphics.Color.Model.HSL
  ( HSL
  -- * Constructors for an HSL color model.
  , pattern ColorHSL
  , pattern ColorHSLA
  , pattern ColorH360SL
  , Color
  , ColorModel(..)
  , hc2rgb
  , hsl2rgb
  , rgb2hsl
  ) where

import Foreign.Storable
import Graphics.Color.Model.HSV (hc2rgb)
import Graphics.Color.Model.Internal
import Graphics.Color.Model.RGB

-----------
--- HSL ---
-----------

-- | Hue, Saturation and Luminance (Lightness) color model.
data HSL

-- | `HSL` color model
newtype instance Color HSL e = HSL (V3 e)

-- | Constructor for @HSL@.
pattern ColorHSL :: e -> e -> e -> Color HSL e
pattern ColorHSL h s l = HSL (V3 h s l)
{-# COMPLETE ColorHSL #-}


-- | Constructor for @HSL@ with alpha channel.
pattern ColorHSLA :: e -> e -> e -> e -> Color (Alpha HSL) e
pattern ColorHSLA h s l a = Alpha (ColorHSL h s l) a
{-# COMPLETE ColorHSLA #-}

-- | Constructor for an HSL color model. Difference from `ColorHSL` is that channels are
-- restricted to `Double` and the hue is specified in 0 to 360 degree range, rather than 0
-- to 1. Note, that this is not checked.
pattern ColorH360SL :: Fractional e => e -> e -> e -> Color HSL e
pattern ColorH360SL h s l <- ColorHSL ((* 360) -> h) s l where
        ColorH360SL h s l = ColorHSL (h / 360) s l
{-# COMPLETE ColorH360SL #-}

-- | `HSL` color model
deriving instance Eq e => Eq (Color HSL e)
-- | `HSL` color model
deriving instance Ord e => Ord (Color HSL e)
-- | `HSL` color model
deriving instance Functor (Color HSL)
-- | `HSL` color model
deriving instance Applicative (Color HSL)
-- | `HSL` color model
deriving instance Foldable (Color HSL)
-- | `HSL` color model
deriving instance Traversable (Color HSL)
-- | `HSL` color model
deriving instance Storable e => Storable (Color HSL e)

-- | `HSL` color model
instance Elevator e => Show (Color HSL e) where
  showsPrec _ = showsColorModel

-- | `HSL` color model
instance Elevator e => ColorModel HSL e where
  type Components HSL e = (e, e, e)
  toComponents (ColorHSL h s l) = (h, s, l)
  {-# INLINE toComponents #-}
  fromComponents (h, s, l) = ColorHSL h s l
  {-# INLINE fromComponents #-}

hsl2rgb :: RealFrac e => Color HSL e -> Color RGB e
hsl2rgb (ColorHSL h s l) = (+ m) <$> hc2rgb h c
  where
    !c = (1 - abs (2 * l - 1)) * s
    !m = l - c / 2
{-# INLINE hsl2rgb #-}


rgb2hsl :: (Ord e, Floating e) => Color RGB e -> Color HSL e
rgb2hsl (ColorRGB r g b) = ColorHSL h s l
  where
    !max' = max r (max g b)
    !min' = min r (min g b)
    !h' | max' == r = (    (g - b) / (max' - min')) / 6
        | max' == g = (2 + (b - r) / (max' - min')) / 6
        | max' == b = (4 + (r - g) / (max' - min')) / 6
        | otherwise = 0
    !h
      | h' < 0 = h' + 1
      | otherwise = h'
    !s
      | max' == 0 || min' == 1 = 0
      | otherwise = (max' - l) / min l (1 - l)
    !l = (max' + min') / 2
{-# INLINE rgb2hsl #-}