//! Color primitives

use derive_more::{From, TryInto};
use crate::constants::*;

#[derive(Clone, Copy, Debug, Eq, PartialEq, From, TryInto)]
pub enum Color {
  Named (Named),
  Raw   ([u8; 4])
}

impl_newtype!{
  Named { Monochrome, Hue }
}
impl_newtype!{
  Terminal { Monochrome, Primary, Secondary }
}
impl_newtype!{
  Hue { Primary, Secondary, Tertiary }
}

impl_primitive!{
  Monochrome {
    Black => BLACK,
    White => WHITE,
    Grey  => GREY
  }
}
impl_primitive!{
  Primary {
    Red   => RED,
    Green => GREEN,
    Blue  => BLUE
  }
}
impl_primitive!{
  Secondary {
    Cyan    => CYAN,
    Yellow  => YELLOW,
    Magenta => MAGENTA
  }
}
impl_primitive!{
  Tertiary {
    Azure      => AZURE,
    Viridian   => VIRIDIAN,
    Chartreuse => CHARTREUSE,
    Orange     => ORANGE,
    Rose       => ROSE,
    Violet     => VIOLET
  }
}

impl Color {
  pub const BLACK   : Self =
    Color::Named (Named::Monochrome (Monochrome::Black));
  pub const WHITE   : Self =
    Color::Named (Named::Monochrome (Monochrome::White));
  pub const GREY    : Self =
    Color::Named (Named::Monochrome (Monochrome::Grey));
  pub const RED     : Self =
    Color::Named (Named::Hue (Hue::Primary (Primary::Red)));
  pub const GREEN   : Self =
    Color::Named (Named::Hue (Hue::Primary (Primary::Green)));
  pub const BLUE    : Self =
    Color::Named (Named::Hue (Hue::Primary (Primary::Blue)));
  pub const CYAN    : Self =
    Color::Named (Named::Hue (Hue::Secondary (Secondary::Cyan)));
  pub const MAGENTA : Self =
    Color::Named (Named::Hue (Hue::Secondary (Secondary::Magenta)));
  pub const YELLOW  : Self =
    Color::Named (Named::Hue (Hue::Secondary (Secondary::Yellow)));

  pub fn raw (&self) -> [u8; 4] {
    match self {
      Color::Named (named) => (*named).into(),
      Color::Raw   (raw)   => *raw
    }
  }

  pub fn named (&self) -> Option <Named> {
    match self {
      Color::Named (named) => Some (named.clone()),
      _ => None
    }
  }
}

macro impl_primitive {
  ( $primitive:ident {
      $($color:ident => $value:ident),+
    }
  ) => {
    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    pub enum $primitive {
      $($color),+
    }
    impl From <$primitive> for [u8; 4] {
      fn from (primitive : $primitive) -> [u8; 4] {
        match primitive {
          $($primitive::$color => $value),+
        }
      }
    }
  }
}

macro impl_newtype {
  ( $newtype:ident {
      $($colors:ident),+
    }
  ) => {
    #[derive(Clone, Copy, Debug, Eq, PartialEq, From, TryInto)]
    pub enum $newtype {
      $($colors ($colors)),+
    }
    impl From <$newtype> for [u8; 4] {
      fn from (newtype : $newtype) -> [u8; 4] {
        match newtype {
          $($newtype::$colors (colors) => colors.into()),+
        }
      }
    }
  }
}
