// Copyright (c) 2017, Marty Mills <daggerbot@gmail.com>
// This software is available under the terms of the zlib license.
// See COPYING.md for more information.

use dnum::Lerp;

use clamp::{Clamp, ClampInto};
use lum::Luminance;

/// Red-green-blue color type.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde_", derive(Serialize, Deserialize))]
#[repr(C)]
pub struct Rgb<T> {
    pub r: T,
    pub g: T,
    pub b: T,
}

impl<T> Rgb<T> {
    pub fn from_lum (l: T) -> Rgb<T>
        where T: Clone
    {
        Rgb { r: l.clone(), g: l.clone(), b: l }
    }

    pub fn new (r: T, g: T, b: T) -> Rgb<T> {
        Rgb { r, g, b }
    }

    pub fn with_alpha (self, a: T) -> Rgba<T> {
        Rgba { r: self.r, g: self.g, b: self.b, a }
    }
}

impl<T> Clamp for Rgb<T>
    where T: Clamp
{
    fn clamp_min () -> Rgb<T> {
        Rgb {
            r: T::clamp_min(),
            g: T::clamp_min(),
            b: T::clamp_min(),
        }
    }

    fn clamp_max () -> Rgb<T> {
        Rgb {
            r: T::clamp_max(),
            g: T::clamp_max(),
            b: T::clamp_max(),
        }
    }
}

impl<S, T> Lerp<T> for Rgb<S>
    where S: Lerp<T>, T: Copy
{
    fn lerp (a: Rgb<S>, b: Rgb<S>, t: T) -> Rgb<S> {
        Rgb {
            r: S::lerp(a.r, b.r, t),
            g: S::lerp(a.g, b.g, t),
            b: S::lerp(a.b, b.b, t),
        }
    }
}

impl<T> Luminance for Rgb<T>
    where T: ClampInto<f64>
{
    fn luminance (self) -> f64 {
        self.r.clamp_into() * 0.2126 + self.g.clamp_into() * 0.7152 + self.b.clamp_into() * 0.0722
    }
}

/// Red-green-blue-alpha color type.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde_", derive(Serialize, Deserialize))]
#[repr(C)]
pub struct Rgba<T> {
    pub r: T,
    pub g: T,
    pub b: T,
    pub a: T,
}

impl<T> Rgba<T> {
    pub fn drop_alpha (self) -> Rgb<T> {
        Rgb { r: self.r, g: self.g, b: self.b }
    }

    pub fn new (r: T, g: T, b: T, a: T) -> Rgba<T> {
        Rgba { r, g, b, a }
    }

    pub fn split_alpha (self) -> (Rgb<T>, T) {
        (Rgb { r: self.r, g: self.g, b: self.b }, self.a)
    }
}

impl<T> Clamp for Rgba<T>
    where T: Clamp
{
    fn clamp_min () -> Rgba<T> {
        Rgba {
            r: T::clamp_min(),
            g: T::clamp_min(),
            b: T::clamp_min(),
            a: T::clamp_min(),
        }
    }

    fn clamp_max () -> Rgba<T> {
        Rgba {
            r: T::clamp_max(),
            g: T::clamp_max(),
            b: T::clamp_max(),
            a: T::clamp_max(),
        }
    }
}

impl<S, T> Lerp<T> for Rgba<S>
    where S: Lerp<T>, T: Copy
{
    fn lerp (a: Rgba<S>, b: Rgba<S>, t: T) -> Rgba<S> {
        Rgba {
            r: S::lerp(a.r, b.r, t),
            g: S::lerp(a.g, b.g, t),
            b: S::lerp(a.b, b.b, t),
            a: S::lerp(a.a, b.a, t),
        }
    }
}

impl<T> Luminance for Rgba<T>
    where T: ClampInto<f64>
{
    fn luminance (self) -> f64 {
        self.drop_alpha().luminance()
    }
}
