////////////////////////////////
// Color Functions

MR4TH_SYMBOL_STATIC V3F32
color_rgb_from_hsv(F32 h, F32 s, F32 v){
  // TODO(allen): study this and determine how to do this best
  
  F32 c = v*s;
  
  F32 h6 = h*6;
  U32 case_n = (U32)(h6);
  F32 h6_mod2_adj = ((case_n|1) - 1);
  F32 h6_mod2 = h6 - h6_mod2_adj;
  
  F32 x = c*(1 - abs_F32(h6_mod2 - 1));
  
  F32 rp,gp,bp;
  switch (case_n){
    case 0:case 6: rp = c; gp = x; bp = 0; break;
    case 1:        rp = x; gp = c; bp = 0; break;
    case 2:        rp = 0; gp = c; bp = x; break;
    case 3:        rp = 0; gp = x; bp = c; break;
    case 4:        rp = x; gp = 0; bp = c; break;
    case 5:        rp = c; gp = 0; bp = x; break;
  }
  
  F32 m = v - c;
  
  F32 r = (rp + m);
  F32 g = (gp + m);
  F32 b = (bp + m);
  
  V3F32 result = {r, g, b};
  return(result);
}

MR4TH_SYMBOL_STATIC V3F32
color_hsv_from_rgb(F32 r, F32 g, F32 b){
  // TODO(allen): study this and determine how to do this best
  
  F32 cmax = Max(r, g);
  cmax     = Max(cmax, b);
  
  F32 cmin = Min(r, g);
  cmin     = Min(cmin, b);
  
  F32 cdif = cmax - cmin;
  
  F32 h = 0.f;
  if (cmax == r){
    h = frac_F32(1.f + 0.166666666666f*(g - b)/cdif);
  }
  else if (cmax == g){
    h = frac_F32(0.333333333f + 0.166666666666f*(b - r)/cdif);
  }
  else if (cmax == b){
    h = frac_F32(0.666666666f + 0.166666666666f*(r - g)/cdif);
  }
  
  F32 s = 0.f;
  if (cmax > 0.f){
    s = cdif/cmax;
  }
  
  F32 v = cmax;
  
  V3F32 result = CLiteral(V3F32){h, s, v};
  return(result);
}

// TODO(allen): RESEARCH: what is really really the most
// fair way to convert from 8-bits to a [0,1] float and back?

MR4TH_SYMBOL_STATIC U32
color_u32_from_v3f32(V3F32 rgb){
  U32 b0 = (U32)(rgb.x*256);
  U32 b1 = (U32)(rgb.y*256);
  U32 b2 = (U32)(rgb.z*256);
  
  U32 c0 = Clamp(0, b0, 255);
  U32 c1 = Clamp(0, b1, 255);
  U32 c2 = Clamp(0, b2, 255);
  
  U32 result = ((c0   <<  0) |
                (c1   <<  8) |
                (c2   << 16) |
                (0xFF << 24));
  return(result);
}

MR4TH_SYMBOL_STATIC U32
color_u32_from_v3f32_f32(V3F32 rgb, F32 a){
  U32 result = color_u32_from_4f32(rgb.x, rgb.y, rgb.z, a);
  return(result);
}

MR4TH_SYMBOL_STATIC U32
color_u32_from_v4f32(V4F32 rgba){
  U32 result = color_u32_from_4f32(rgba.x, rgba.y, rgba.z, rgba.w);
  return(result);
}

MR4TH_SYMBOL_STATIC U32
color_u32_from_4f32(F32 x0, F32 x1, F32 x2, F32 x3){
  U32 b0 = (U32)(x0*256);
  U32 b1 = (U32)(x1*256);
  U32 b2 = (U32)(x2*256);
  U32 b3 = (U32)(x3*256);
  
  U32 c0 = Clamp(0, b0, 255);
  U32 c1 = Clamp(0, b1, 255);
  U32 c2 = Clamp(0, b2, 255);
  U32 c3 = Clamp(0, b3, 255);
  
  U32 result = ((c0 <<  0) |
                (c1 <<  8) |
                (c2 << 16) |
                (c3 << 24));
  
  return(result);
}

MR4TH_SYMBOL_STATIC V4F32
color_4f32_from_u32(U32 c){
  V4F32 result = {0};
  result.x = ((c >>  0)&0xFF)/255.f;
  result.y = ((c >>  8)&0xFF)/255.f;
  result.z = ((c >> 16)&0xFF)/255.f;
  result.w = ((c >> 24)&0xFF)/255.f;
  return(result);
}

MR4TH_SYMBOL_STATIC F32
color_linear_from_srgb_single(F32 x){
  F32 r = 0.f;
  if (x <= 0.04045f){
    r = x/12.92f;
  }
  else{
    r = pow_F32(((x + 0.055f)/1.055f), 2.4f);
  }
  return(r);
}

MR4TH_SYMBOL_STATIC F32
color_srgb_from_linear_single(F32 x){
  F32 r = 0.f;
  if (x <= 0.0031308f){
    r = x*12.92f;
  }
  else{
    r = pow_F32(x, 1/2.4f)*1.055f - 0.055f;
  }
  return(r);
}

MR4TH_SYMBOL_STATIC U32
color_linear_from_srgb_u32(U32 srgb){
  V4F32 c = color_4f32_from_u32(srgb);
  V4F32 d = c;
  d.x = color_linear_from_srgb_single(d.x);
  d.y = color_linear_from_srgb_single(d.y);
  d.z = color_linear_from_srgb_single(d.z);
  U32 result = color_u32_from_4f32(v4_expanded(d));
  return(result);
}

MR4TH_SYMBOL_STATIC U32
color_srgb_from_linear_u32(U32 lin){
  V4F32 c = color_4f32_from_u32(lin);
  V4F32 d = c;
  d.x = color_srgb_from_linear_single(d.x);
  d.y = color_srgb_from_linear_single(d.y);
  d.z = color_srgb_from_linear_single(d.z);
  U32 result = color_u32_from_4f32(v4_expanded(d));
  return(result);
}

