#pragma once

#include "ofMain.h"
#include "ofxFXObject.h"

class ofxFront2 : public ofxFXObject {
public:

	ofxFront2() {
		passes = 1;
		internalFormat = GL_RGBA32F;
		fragmentShader = STRINGIFY(
			uniform float     time;           // shader playback time (in seconds)
		uniform vec3      mouse;
		uniform vec2      resolution;           // viewport resolution (in pixels)
		uniform sampler2D tex0;          // BufC

												// fabrice's rotation matrix
		vec2 V;

		// iq's HSV
		vec3 hsv2rgb(in vec3 c) {
			vec3 rgb = clamp(abs(mod(c.x*6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
			rgb = rgb*rgb*(3.0 - 2.0*rgb);
			return c.z * mix(vec3(1.0), rgb, c.y);
		}


		// hexagonal distance
		float hexDist(vec2 p) {
			float dx = abs(p.x);
			float dy = abs(p.y);
			return max(dx + dy*(1.0 / tan(3.14159265359 / 3.0)), max(dx, dy*(1.0 / sin(3.14159265359 / 3.0))));
		}

		vec4 fractal(in vec2 p) {

			// keep current scale
			float scale = 1.0;

			// used to smoothstep
			float aliasBase = 1.0 / resolution.y;

			// accumulated alpha
			float alpha = 0.0;
			// accumulated color
			vec3 color = vec3(0.0);

			float ss = texture2D(tex0, abs(2.0*fract(p - 0.5) - 2.0)).b*time*0.01;

			for (int i = 0; i < 11; i++) {

				// scale
				float s = 2.0 + cos(time*0.2);

				// repeat axis according to scale ala TEXTURE_ADDRESS_MIRROR
				p = 1.5 - abs(s*fract(p - 0.5) + cos(time*0.01) + distance(p.x*1.01*cos(s*0.1 + time*0.1), p.y + s*time*0.01) - s*0.5);

				// fold
				float theta = float(i) * 3.14159265359 * 2.125;
				p.x = abs(p.x);
				p *= mat2(V = sin(vec2(1.57, 0) +theta), -V.y, V.x);

				// update scale
				scale *= s + ss;

				// jump first steps cause they're less interesting
				if (i < 4) continue;

				// texture

				// borders
				vec2 uv = abs(p);
				float delt1 = abs((hexDist(uv) - 0.6) - 0.1);
				float delt2 = min(length(uv) - 0.2, min(uv.x, uv.y));
				float m = min(delt1, delt2) - ss*0.001*time;
				float alias = aliasBase*0.5*scale;
				float f = (1.0 - smoothstep(0.10, 0.10 + alias, m))*0.4 + (1.0 - smoothstep(0.11, 0.22, m))*0.6;
				f = clamp(f, 0.0, 1.0);

				// pulse
				float r = length(uv) / 0.707106;
				float t = mod(time*2.0, float(11 - 10)*16.0);
				t -= float(i);
				r += 1.0;
				r -= t;
				r = (r)*step(r, 1.0);
				r = smoothstep(0.0, 0.8, r) - smoothstep(0.9, 1.0, r);

				// mix colors
				vec3 c = vec3(0.9 - smoothstep(time*0.001, 0.06 + alias, m));
				float lev = smoothstep(4.0, float(11 - 1), float(i));
				vec3 hue = hsv2rgb(vec3(time*0.03 + lev*0.4, 0.5, 1.0));
				c = c*hue*0.5;
				c += c*hue*r;
				c += c*r*1.0;

				// front to back compositing
				color = (1.0 - alpha)*c + color*0.9;
				alpha = (1.0 - alpha)*f + alpha*0.7;

			}

			return vec4(color, alpha);
		}

		void main() {

			vec2 uv = gl_FragCoord.xy / resolution.xy * 2.0 - 1.0;
			if (uv.x < 0.0) uv.x = 0.0 - uv.x;
			vec2 p = uv;
			uv.x *= resolution.x / resolution.y;


			uv += vec2(0.4487*0.04, 0.17567*0.01)*(time*0.1);

			uv *= 0.05 + (cos(time*0.05)*0.01);

			vec4 frac = fractal(uv)*1.0;
			frac += (0.2 + abs(cos(time*0.01 + p.y + p.x)*0.2)) - fractal(uv + uv.x*1.1 + time*0.01)*cos(time*0.001);

			// mix fractal with a grey background
			gl_FragColor = clamp(vec4(mix(vec3(0.0), frac.rgb, frac.a), 1.0),vec4(0.0),vec4(0.9));

		}


		);

	};
	void update(float time) {

		shader.setUniform1f("time", time*0.5);
		shader.setUniform2f("resolution", (float)width*1.0, (float)height*1.0);
		shader.setUniform2f("mouse", (float)ofGetMouseX() / width, (float)ofGetMouseY() / height);
	};

};

