-- | Predefined and custom colors.
module Graphics.Gloss.Data.Color
        ( -- ** Color data type
          Color
        , makeColor
        , makeColorI
        , rgbaOfColor

          -- ** Color functions
        , mixColors
        , addColors
        , dim,   bright
        , light, dark

        , withRed
        , withGreen
        , withBlue
        , withAlpha

          -- ** Pre-defined colors
        , greyN,  black,  white

          -- *** Primary
        , red,    green,  blue

          -- *** Secondary
        , yellow,     cyan,       magenta
        
          -- *** Tertiary
        , rose,   violet, azure, aquamarine, chartreuse, orange
        )
where
import Graphics.Gloss.Rendering



-- Color functions ------------------------------------------------------------
-- | Mix two colors with the given ratios.
mixColors 
        :: Float        -- ^ Proportion of first color.
        -> Float        -- ^ Proportion of second color.
        -> Color        -- ^ First color.
        -> Color        -- ^ Second color.
        -> Color        -- ^ Resulting color.

mixColors m1 m2 c1 c2
 = let  (r1, g1, b1, a1) = rgbaOfColor c1
        (r2, g2, b2, a2) = rgbaOfColor c2

        -- Normalise mixing proportions to ratios.
        m12 = m1 + m2
        m1' = m1 / m12
        m2' = m2 / m12

        -- Colors components should be added via sum of squares,
        -- otherwise the result will be too dark.
        r1s = r1 * r1;    r2s = r2 * r2
        g1s = g1 * g1;    g2s = g2 * g2
        b1s = b1 * b1;    b2s = b2 * b2

   in   makeColor
                (sqrt (m1' * r1s + m2' * r2s))
                (sqrt (m1' * g1s + m2' * g2s))
                (sqrt (m1' * b1s + m2' * b2s))
                ((m1 * a1   + m2 * a2) / m12)


-- | Add RGB components of a color component-wise,
--   then normalise them to the highest resulting one. 
--   The alpha components are averaged.
addColors :: Color -> Color -> Color
addColors c1 c2
 = let  (r1, g1, b1, a1) = rgbaOfColor c1
        (r2, g2, b2, a2) = rgbaOfColor c2

   in   normalizeColor 
                (r1 + r2)
                (g1 + g2)
                (b1 + b2)
                ((a1 + a2) / 2)


-- | Make a dimmer version of a color, scaling towards black.
dim :: Color -> Color
dim c
 = let  (r, g, b, a)    = rgbaOfColor c
   in   makeColor (r / 1.2) (g / 1.2) (b / 1.2) a

        
-- | Make a brighter version of a color, scaling towards white.
bright :: Color -> Color
bright c
 = let  (r, g, b, a)    = rgbaOfColor c
   in   makeColor (r * 1.2) (g * 1.2) (b * 1.2) a


-- | Lighten a color, adding white.
light :: Color -> Color
light c
 = let  (r, g, b, a)    = rgbaOfColor c
   in   makeColor (r + 0.2) (g + 0.2) (b + 0.2) a
        
        
-- | Darken a color, adding black.
dark :: Color -> Color
dark c
 = let  (r, g, b, a)    = rgbaOfColor c
   in   makeColor (r - 0.2) (g - 0.2) (b - 0.2) a


-------------------------------------------------------------------------------
-- | Set the red value of a `Color`.
withRed :: Float -> Color -> Color
withRed r c
 = let  (_, g, b, a) = rgbaOfColor c
   in   makeColor r g b a


-- | Set the green value of a `Color`.
withGreen :: Float -> Color -> Color
withGreen g c
 = let  (r, _, b, a) = rgbaOfColor c
   in   makeColor r g b a


-- | Set the blue value of a `Color`.
withBlue :: Float -> Color -> Color
withBlue b c
 = let  (r, g, _, a) = rgbaOfColor c
   in   makeColor r g b a


-- | Set the alpha value of a `Color`.
withAlpha :: Float -> Color -> Color
withAlpha a c
 = let  (r, g, b, _) = rgbaOfColor c
   in   makeColor r g b a


-- Pre-defined Colors ---------------------------------------------------------
-- | A greyness of a given order.
-- 
--   Range is 0 = black, to 1 = white.
greyN   :: Float -> Color
greyN n         = makeRawColor n   n   n   1.0

black, white :: Color
black           = makeRawColor 0.0 0.0 0.0 1.0
white           = makeRawColor 1.0 1.0 1.0 1.0

-- Colors from the additive color wheel.
red, green, blue :: Color
red             = makeRawColor 1.0 0.0 0.0 1.0
green           = makeRawColor 0.0 1.0 0.0 1.0
blue            = makeRawColor 0.0 0.0 1.0 1.0

-- secondary
yellow, cyan, magenta :: Color
yellow          = addColors red   green
cyan            = addColors green blue
magenta         = addColors red   blue

-- tertiary
rose, violet, azure, aquamarine, chartreuse, orange :: Color
rose            = addColors red     magenta
violet          = addColors magenta blue
azure           = addColors blue    cyan
aquamarine      = addColors cyan    green
chartreuse      = addColors green   yellow
orange          = addColors yellow  red


-------------------------------------------------------------------------------
-- | Normalise a color to the value of its largest RGB component.
normalizeColor :: Float -> Float -> Float -> Float -> Color
normalizeColor r g b a
 = let  m               = maximum [r, g, b]
   in   makeColor (r / m) (g / m) (b / m) a