precision mediump float ;

varying vec2      vPosition   ;

uniform bool      uUseFeature ;
uniform sampler2D uImage      ;
uniform int       uRange      ;
uniform int       uTHold      ;
uniform int       uStates     ;
uniform int       uNType      ;

const float dt1 = 1.0 / 512.0 ;
const float ds1 = 1.0 / 512.0 ;
const float dt2 = 2.0 / 512.0 ;
const float ds2 = 2.0 / 512.0 ;
const float dt3 = 3.0 / 512.0 ;
const float ds3 = 3.0 / 512.0 ;

float hue   ;
float sat   ;
float val   ;
float dhue  ;
float dsat  ;
float dval  ;
float edge  ;

int   nh    ;
int   ns    ;
int   nv    ;
int   kh    ;
int   ks    ;
int   kv    ;

vec4  playfun    (vec2)         ;
void  makeCountM (vec2)         ;
void  makeCountN (vec2)         ;
void  makeCount  (vec2)         ;
float makeHue    (float, int)   ;
float makeSat    (float, int)   ;
float makeVal    (float, int)   ;
vec3  rgb2hsv    (vec3)         ;
vec3  hsv2rgb    (vec3)         ;

/** ------------------------------------------ */
void main(void)
{
  if (uUseFeature)
    gl_FragColor = playfun (vPosition) ;
  else
    gl_FragColor = texture2D (uImage, vPosition) ;
}

/** ------------------------------------------ */
vec4 playfun (vec2 txtCoord)
{
  vec4 myrgb = texture2D (uImage, txtCoord) ;
  vec3 myhsv = rgb2hsv (myrgb.rgb) ;

  hue  = myhsv.x ;
  sat  = myhsv.y ;
  val  = myhsv.z ;
  dhue = 360.0 / float (uStates) ;
  dsat = 1.0   / float (uStates) ;
  dval = 1.0   / float (uStates) ;
  edge = float (uStates -1)      ;

  nh   = 0 ;
  ns   = 0 ;
  nv   = 0 ;
  kh   = int (clamp (floor (hue /dhue), 0.0, edge)) +1 ;
  ks   = int (clamp (floor (sat /dsat), 0.0, edge)) +1 ;
  kv   = int (clamp (floor (val /dval), 0.0, edge)) +1 ;

  if (kh >= uStates) kh = 0 ;
  if (ks >= uStates) ks = 0 ;
  if (kv >= uStates) kv = 0 ;

  if (uNType == 0)
    makeCountM (txtCoord) ;
  else
    makeCountN (txtCoord) ;

  hue = clamp (makeHue (hue, nh), 0.0, 360.0) ;
  sat = clamp (makeSat (sat, ns), 0.0, 1.0)   ;
/**
  val = clamp (makeVal (val, nv), 0.0, 1.0)   ;
*/
  return vec4 (hsv2rgb (vec3(hue, sat, val)), 1.0) ;
}
/** ------------------------------------------
 *  Extended Moore (range 1 - 3)
 *  ------------------------------------------
 */
void makeCountM (vec2 txtCoord)
{
  makeCount (txtCoord + vec2(-ds1, -dt1)) ;
  makeCount (txtCoord + vec2(-ds1,  0.0)) ;
  makeCount (txtCoord + vec2(-ds1,  dt1)) ;
  makeCount (txtCoord + vec2( 0.0, -dt1)) ;
  makeCount (txtCoord + vec2( 0.0,  dt1)) ;
  makeCount (txtCoord + vec2( ds1, -dt1)) ;
  makeCount (txtCoord + vec2( ds1,  0.0)) ;
  makeCount (txtCoord + vec2( ds1,  dt1)) ;

  if (uRange > 1)
  { makeCount (txtCoord + vec2(-ds2, -dt2)) ;
    makeCount (txtCoord + vec2(-ds2, -dt1)) ;
    makeCount (txtCoord + vec2(-ds2,  0.0)) ;
    makeCount (txtCoord + vec2(-ds2,  dt1)) ;
    makeCount (txtCoord + vec2(-ds2,  dt2)) ;

    makeCount (txtCoord + vec2(-ds1, -dt2)) ;
    makeCount (txtCoord + vec2(-ds1,  dt2)) ;
    makeCount (txtCoord + vec2( 0.0, -dt2)) ;
    makeCount (txtCoord + vec2( 0.0,  dt2)) ;
    makeCount (txtCoord + vec2( ds1, -dt2)) ;
    makeCount (txtCoord + vec2( ds1,  dt2)) ;

    makeCount (txtCoord + vec2( ds2, -dt2)) ;
    makeCount (txtCoord + vec2( ds2, -dt1)) ;
    makeCount (txtCoord + vec2( ds2,  0.0)) ;
    makeCount (txtCoord + vec2( ds2,  dt1)) ;
    makeCount (txtCoord + vec2( ds2,  dt2)) ;
  }
  if (uRange > 2)
  { makeCount (txtCoord + vec2(-ds3, -dt3)) ;
    makeCount (txtCoord + vec2(-ds3, -dt2)) ;
    makeCount (txtCoord + vec2(-ds3, -dt1)) ;
    makeCount (txtCoord + vec2(-ds3,  0.0)) ;
    makeCount (txtCoord + vec2(-ds3,  dt1)) ;
    makeCount (txtCoord + vec2(-ds3,  dt2)) ;
    makeCount (txtCoord + vec2(-ds3,  dt3)) ;

    makeCount (txtCoord + vec2(-ds2, -dt3)) ;
    makeCount (txtCoord + vec2(-ds2,  dt3)) ;
    makeCount (txtCoord + vec2(-ds1, -dt3)) ;
    makeCount (txtCoord + vec2(-ds1,  dt3)) ;
    makeCount (txtCoord + vec2( 0.0, -dt3)) ;
    makeCount (txtCoord + vec2( 0.0,  dt3)) ;
    makeCount (txtCoord + vec2( ds1, -dt3)) ;
    makeCount (txtCoord + vec2( ds1,  dt3)) ;
    makeCount (txtCoord + vec2( ds2, -dt3)) ;
    makeCount (txtCoord + vec2( ds2,  dt3)) ;

    makeCount (txtCoord + vec2( ds3, -dt3)) ;
    makeCount (txtCoord + vec2( ds3, -dt2)) ;
    makeCount (txtCoord + vec2( ds3, -dt1)) ;
    makeCount (txtCoord + vec2( ds3,  0.0)) ;
    makeCount (txtCoord + vec2( ds3,  dt1)) ;
    makeCount (txtCoord + vec2( ds3,  dt2)) ;
    makeCount (txtCoord + vec2( ds3,  dt3)) ;
  }
}
/** ------------------------------------------
 *  Extended von Neumann (range 1 - 3)
 *  ------------------------------------------
 */
void makeCountN (vec2 txtCoord)
{
  makeCount (txtCoord + vec2(-ds1,  0.0)) ;
  makeCount (txtCoord + vec2(0.0,  -dt1)) ;
  makeCount (txtCoord + vec2(0.0,   dt1)) ;
  makeCount (txtCoord + vec2( ds1,  0.0)) ;

  if (uRange > 1)
  { makeCount (txtCoord + vec2(-ds2,  0.0)) ;
    makeCount (txtCoord + vec2(0.0,  -dt2)) ;
    makeCount (txtCoord + vec2(0.0,   dt2)) ;
    makeCount (txtCoord + vec2( ds2,  0.0)) ;

    makeCount (txtCoord + vec2(-ds1, -dt1)) ;
    makeCount (txtCoord + vec2(-ds1,  dt1)) ;
    makeCount (txtCoord + vec2( ds1, -dt1)) ;
    makeCount (txtCoord + vec2( ds1,  dt1)) ;
  }
  if (uRange > 2)
  { makeCount (txtCoord + vec2(-ds3,  0.0)) ;
    makeCount (txtCoord + vec2(0.0,  -dt3)) ;
    makeCount (txtCoord + vec2(0.0,   dt3)) ;
    makeCount (txtCoord + vec2( ds3,  0.0)) ;

    makeCount (txtCoord + vec2(-ds2, -dt1)) ;
    makeCount (txtCoord + vec2(-ds2,  dt1)) ;
    makeCount (txtCoord + vec2(-ds1, -dt2)) ;
    makeCount (txtCoord + vec2(-ds1,  dt2)) ;

    makeCount (txtCoord + vec2( ds2, -dt1)) ;
    makeCount (txtCoord + vec2( ds2,  dt1)) ;
    makeCount (txtCoord + vec2( ds1, -dt2)) ;
    makeCount (txtCoord + vec2( ds1,  dt2)) ;
  }

}
/** ------------------------------------------ */
void makeCount (vec2 txtcoff)
{
  vec4 myrgb = texture2D (uImage, txtcoff) ;
  vec3 myhsv = rgb2hsv (myrgb.rgb) ;


  if (kh == int (clamp (floor (myhsv.x / dhue), 0.0, edge)))
      nh++ ;
  if (ks == int (clamp (floor (myhsv.y / dsat), 0.0, edge)))
      ns++ ;
  if (kv == int (clamp (floor (myhsv.z / dval), 0.0, edge)))
      nv++ ;
}

/** ------------------------------------------ */
float makeHue (float hval, int num)
{
  float ret = (num >= uTHold) ? hval + dhue : hval ;

  return (ret > 360.0) ? ret - 360.0 : ret ;
}
/** ------------------------------------------ */
float makeSat (float sval, int num)
{
  float ret = (num >= uTHold) ? sval + dsat : sval ;

  return (ret > 1.0) ? ret - 1.0 : ret ;
}
/** ------------------------------------------ */
float makeVal (float vval, int num)
{
  float ret = (num >= uTHold) ? vval + dval : vval ;

  return (ret > 1.0) ? ret - 1.0 : ret ;
}
/** ------------------------------------------ */
vec3 rgb2hsv (vec3 c)
{
  float mx = max (c.r, max (c.g, c.b)) ;
  float mn = min (c.r, min (c.g, c.b)) ;
  float dm = mx - mn ;

  vec3 ret = vec3 (0.0, 0.0, mx) ;

  if (dm > 0.0)
  { ret.y = dm / mx ;

    if (mx == c.r)
      ret.x = ((c.g - c.b) / dm) * 60.0 ;
    else if (mx == c.g)
      ret.x = ((c.b - c.r) / dm) * 60.0 + 120.0 ;
    else
      ret.x = ((c.r - c.g) / dm) * 60.0 + 240.0 ;

    if (ret.x < 0.0) ret.x += 360.0 ;
  }
  return ret ;
}
/** ------------------------------------------ */
vec3 hsv2rgb (vec3 c)
{
  int   n = int (floor (c.x / 60.0)) ;
  float f = fract (c.x / 60.0) ;
  float s = c.y ;
  float v = c.z ;

  float p  = v * (1.0 - s)             ;
  float q  = v * (1.0 - s * f)         ;
  float t  = v * (1.0 - s * (1.0 - f)) ;

  vec3 ret = vec3 (v, t, p) ;

  if      (n == 5) ret = vec3 (v, p, q) ;
  else if (n == 4) ret = vec3 (t, p, v) ;
  else if (n == 3) ret = vec3 (p, q, v) ;
  else if (n == 2) ret = vec3 (p, v, t) ;
  else if (n == 1) ret = vec3 (q, v, p) ;

  return ret ;
}

/** E O F */
