precision mediump float;

varying vec2 v_texCoord;
uniform vec2 z;
uniform vec2 dir;
uniform bool refl;

uniform vec2 a1;
uniform vec2 a2;
uniform vec2 a3;
uniform vec2 a4;
uniform float thick;
uniform float thick2;
uniform float zoom;
uniform bool lines;
uniform bool twins;

float cross(vec2 a, vec2 b) {
  return a.x*b.y-a.y*b.x;
}

void get_t(out float t, out bool ok, vec2 o, vec2 d, vec2 a, vec2 b) {
  t=cross(b-a,a-o)/cross(b-a,d-o);
  if(!(0.0<=t && t<1.0)) { ok=false; return; }
  float x=cross(d-o,o-a)/cross(d-o,b-a);
  ok=(0.0<x && x<1.0);
}

vec2 ref(vec2 v,vec2 u) {
  vec2 p = (dot(v,u)/dot(u,u))*u;
  return 2.0*p-v;
}

void reflect(inout vec2 o, inout vec2 oo, inout vec2 d, vec2 a, vec2 b) {
  float t=-cross(b-a,o-a)/cross(b-a,d-o);
  o=o+(d-o)*t;
  d=o+ref(d-o,b-a);
  oo=o+ref(oo-o,b-a);
}

vec2 mul(vec2 a, vec2 b) {
  return vec2(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
}

vec2 mul2(vec2 a, vec2 b) { // a*conj(b)
  return vec2(a.x*b.x+a.y*b.y,-a.x*b.y+a.y*b.x);
}

float Norm(vec2 v) {
  return v.x*v.x+v.y*v.y;
}

float Norm2(vec2 v) {
  return max(abs(v.x),abs(v.y));
}

bool test_align(vec2 oo,vec2 d,vec2 a,float w) {
  vec2 oa=a-oo;
  vec2 od=d-oo;
  if(dot(a-d,oa)>0.0) return false;
  float aux=cross(oa,od);
  return(aux*aux<w*w*Norm(oa));
}

vec2 reflect2(vec2 o, vec2 a, vec2 b) {
  return a+ref(o-a,b-a);
}

void character(inout bool ok, inout vec4 col, vec2 u, vec2 z, vec2 d2) {
  vec2 v=vec2(0.0,-2.0*thick2);
  vec2 w=mul2(u-z,d2);
  if(refl) w.y = -w.y;
  if(Norm(w)<thick2*thick2) {
    ok=true; col=vec4(0.6,0.2,0.8,1.0);
  }
  vec2 a=w-v;
  if(abs(a.y)<thick2 && 1.5*abs(a.x)+a.y<thick2) {
    ok=true; col=vec4(0.6,0.2,0.8,1.0); //col=vec4(0.1,0.5,0.15,1.0);
  }
}

void main() {
  vec4 col;
  vec2 temp=v_texCoord*zoom;
  if(!twins && Norm(temp)<thick2*thick2)
    col=vec4(0.6,0.2,0.8,1.0);
  else {
    if(refl) temp.y=-temp.y;
    vec2 dest=mul(dir,temp)+z;
    vec2 orig=z;
    vec2 old_orig=z;
    int c=0;
    int m=0,nm=0;
    bool ok,on_line=false;
    float t,tmin,tz=thick*zoom;
    int ct;
    tmin=-1.0;
    for(int count=0; count<66; count++) {
      ct=count;
      tmin=2.0;
      nm=0;
      if(lines) {
        if(test_align(old_orig,dest,a1,tz)) { on_line=true; break;}
        if(test_align(old_orig,dest,a2,tz)) { on_line=true; break;}
        if(test_align(old_orig,dest,a3,tz)) { on_line=true; break;}
        if(test_align(old_orig,dest,a4,tz)) { on_line=true; break;}
      }
      if(m!=1) { get_t(t,ok,orig,dest,a1,a2); }
      if(ok && t<tmin) { nm=1; tmin=t; };
      if(m!=2) { get_t(t,ok,orig,dest,a2,a3); }
      if(ok && t<tmin) { nm=2; tmin=t; };
      if(m!=3) { get_t(t,ok,orig,dest,a3,a4); }
      if(ok && t<tmin) { nm=3; tmin=t; };
      if(m!=4) { get_t(t,ok,orig,dest,a4,a1); }
      if(ok && t<tmin) { nm=4; tmin=t; };
    
      m=nm;
      if(m==0) break;
      c=1-c;
      if(m==1) reflect(orig,old_orig,dest,a1,a2);
      else if(m==2) reflect(orig,old_orig,dest,a2,a3);
      else if(m==3) reflect(orig,old_orig,dest,a3,a4);
      else if(m==4) reflect(orig,old_orig,dest,a4,a1);
    }
    if(on_line) col=vec4(0.0,0.0,0.0,1.0);
    else {
      if(twins) {
        vec2 d2=dir;
        character(ok,col,dest,z,d2);
        vec2 u=reflect2(dest,a1,a2);
        character(ok,col,u,z,d2);
        u=reflect2(dest,a2,a3);
        character(ok,col,u,z,d2);
        u=reflect2(dest,a3,a4);
        character(ok,col,u,z,d2);
        u=reflect2(dest,a4,a1);
        character(ok,col,u,z,d2);
      }
      if(!ok) {
        float x=Norm(dest-(a1+a2+a3+a4)/4.0);
        float s=0.0;
        float t=(dest.x+0.5*dest.y)*20.0;
        t=t-floor(t);
        if(t<0.5) s=1.0;
        if((c==0) != refl) {
          col=vec4(0.9+s*0.07,0.8+s*0.06,0.5+10.0*x,1.0);
        }
        else {
          col=vec4(0.83-10.0*x-0.066*s,0.7-10.0*x-0.066*s,0.4-0.15*s,1.0);
        }
      }
    }
  }
  gl_FragColor = col;
}
