"use strict";

import * as THREE from 'three';
import * as BufferGeometryUtils from 'BufferGeometryUtils';
import { gsap } from 'gsap';
import { hotline } from 'hotline';

const fragmentShaderCode = `
  precision highp float;
  uniform vec2 u_resolution;
  uniform float u_time;
  varying vec2 vUv;

  #define EPSILON 1e-6
  #define PI 3.14159265359
  #define ITERATIONS 18.0

  mat2 rotate2d(float angle){
    return mat2(cos(angle), -sin(angle),
                sin(angle), cos(angle));
  }

  void main() {
    vec2 uv = (vUv * 2.0 - 1.0) * (u_resolution / max(u_resolution.x, u_resolution.y));
    vec2 u = uv * 0.25;
    vec4 o = vec4(0.5, 1.0, 1.5, 0.0);
    vec4 z = o;
    vec2 v_internal = vec2(0.0);
    float a = 0.6;
    float t = u_time * 0.8;

    for (float i = 0.0; i < ITERATIONS; i++)
    {
      float u_dot = dot(u, u);
      float denom_u = 0.6 - u_dot;
      denom_u += sign(denom_u) * EPSILON;

      vec2 sin_arg = (1.4 * u / denom_u) - (7.0 * u.yx * cos(t*0.2)) + t * 1.1 + v_internal * 0.3;
      vec2 length_arg = (1.0 + i * 0.1 + a * 0.2) * sin(sin_arg);
      float len = length(length_arg);
      float safe_len_divisor = max(len, EPSILON);
      // float safe_len_divisor = 1.0;

      o += (1.0 + sin(z * 0.9 + t * 1.2 + i * 0.1)) / safe_len_divisor * (1.0 + i*0.02);

      v_internal = 0.9 * v_internal + 0.15 * sin(t * 1.5 + u * 4.0 - o.xy * 0.2);
      v_internal = clamp(v_internal, -1.0, 1.0);

      a += 0.035;
      float angle = i * 0.1 + t * 0.05 + a * 0.2;
      mat2 rot_mat = rotate2d(angle);
      u *= rot_mat;

      float o_dot = dot(o.xyz, o.xyz);
      float feedback_scale = 0.5 + 0.5 * sin(o_dot * 0.02 + t * 0.3);

      u += sin(60.0 * dot(u,u) * cos(80.0 * u.yx + t * 1.2)) / 2.5e2
          + 0.15 * a * u * feedback_scale
          + cos(o.xy * 0.5 + t * 1.1 + v_internal * 0.8) / 3.5e2;

      u += rotate2d(v_internal.x * 0.01) * vec2(0.0001, 0.0);
    }

    vec3 base_color = 0.5 + 0.5 * cos(o.xyz * 0.8 + t * 0.15 + vec3(0.0, PI * 0.66, PI * 1.33));
    vec2 detail_coord = u * 5.0 + v_internal * 1.0;
    float detail_pattern = smoothstep(0.3, 0.7, fract(detail_coord.x * cos(t*0.1) + detail_coord.y * sin(t*0.1)));
    vec3 detail_color = vec3(detail_pattern * 0.8 + 0.2);
    float mix_factor = clamp(length(o.xyz) * 0.1 - 0.1, 0.0, 1.0);
    vec3 final_color = mix(base_color, detail_color * base_color, mix_factor);
    final_color.rg += u.xy * 0.05;
    // float dist_from_center = length(vUv - 0.5);
    float dist_from_center = 0.0;
    final_color *= pow(1.0 - dist_from_center * 1.2, 2.0);
    gl_FragColor = vec4(clamp(final_color, 0.0, 1.0), 1.0);
  }
`;

const vertexShaderCode = `
  varying vec2 vUv;
  void main() {
    vUv = uv;
    gl_Position = vec4( position, 1.0 );
  }
`;

let scene, camera, renderer, mesh, material, clock;
let container;

const uniforms = {
	u_time: { value: 0.0 },
	u_resolution: { value: new THREE.Vector2() },
};

function init() {
	container = document.getElementById("ebashilovo");
	clock = new THREE.Clock();

	renderer = new THREE.WebGLRenderer();
	renderer.setSize(container.offsetWidth, container.offsetHeight);
	renderer.setPixelRatio(window.devicePixelRatio);
	container.appendChild(renderer.domElement);

	scene = new THREE.Scene();

	camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);

	const geometry = new THREE.PlaneGeometry(2, 2);

	material = new THREE.ShaderMaterial({
		uniforms: uniforms,
		vertexShader: vertexShaderCode,
		fragmentShader: fragmentShaderCode,
	});

	mesh = new THREE.Mesh(geometry, material);
	scene.add(mesh);

	uniforms.u_resolution.value.x = container.offsetWidth;
	uniforms.u_resolution.value.y = container.offsetHeight;

	window.addEventListener("resize", onWindowResize);

	renderer.setAnimationLoop(animate);
}

function onWindowResize() {
	renderer.setSize(container.offsetWidth, container.offsetHeight);
	uniforms.u_resolution.value.x = container.offsetWidth;
	uniforms.u_resolution.value.y = container.offsetHeight;
	material.uniforms.u_resolution.value.set(
		container.offsetWidth,
		container.offsetHeight
	);
}

function animate() {
	uniforms.u_time.value = clock.getElapsedTime();
	renderer.render(scene, camera);
}

init();

// Initializing an instance of hotline manually
const authors = new hotline(document.getElementById("authors"), true);

// Initializing settings of the hotline instance
authors.alive = true;
authors.step = -1;
authors.interval = 10;
authors.wheel = false;
authors.movable = true;
authors.hover = true;

// Starting the banners hotline instance
authors.start();

// Initializing an instance of hotline manually
const buttons = new hotline(document.getElementById("buttons"), true);

// Initializing settings of the hotline instance
buttons.alive = true;
buttons.step = -1;
buttons.interval = 20;
buttons.wheel = false;
// buttons.movable = false;
buttons.hover = true;
buttons.vertical = true;

// Starting the banners hotline instance
buttons.start();

// Initializing an instance of hotline manually
// const questions = new hotline(document.getElementById("questions"), true);

// // Initializing settings of the hotline instance
// questions.alive = true;
// questions.step = -1;
// questions.interval = 20;
// questions.wheel = false;
// questions.movable = false;
// questions.hover = false;
// questions.vertical = true;

// // Starting the hotline instance
// questions.start();

const order = document.getElementById("order");
order.style.left = Math.random() * (window.innerWidth - 80) + 80 + "px";
order.style.top = Math.random() * (window.innerHeight - 50) + 50 + "px";

onmousemove = function (e) {
	order.style.left = e.pageX - 45 + "px";
	order.style.top = e.pageY - 13 + "px";
};

const order_repeat = (value) => {
	if (value === 1) order.style.removeProperty("pointer-events");
	else order.style.setProperty("pointer-events", "none");

	order.style.opacity = value;

	setTimeout(
		(fn) => order_repeat(Math.round(Math.random())),
		Math.random() * 120 * 1000
	);
};

order_repeat();

function replaceVerticalScrollByHorizontal(event) {
	if (event.deltaY != 0) {
		// manually scroll horizonally instead
		window.scroll(window.scrollX + event.deltaY, window.scrollY);

		// prevent vertical scroll
		event.preventDefault();
	}
	return;
}

/* SPIRAL */

window.addEventListener("wheel", replaceVerticalScrollByHorizontal);

const n = 19;
const rots = [
	{ ry: 270, a: 0.5 },
	{ ry: 0, a: 0.85 },
	{ ry: 90, a: 0.4 },
	{ ry: 180, a: 0.0 },
];

gsap.set(".face", {
	z: 200,
	rotateY: (i) => rots[i].ry,
	transformOrigin: "50% 50% -201px",
});

for (let i = 0; i < n; i++) {
	let die = document.querySelector(".die");
	let cube = die.querySelector(".cube");

	if (i > 0) {
		let clone = document.querySelector(".die").cloneNode(true);
		document.querySelector(".tray").append(clone);
		cube = clone.querySelector(".cube");
	}

	gsap
		.timeline({
			repeat: -1,
			yoyo: true,
			defaults: { ease: "power3.inOut", duration: 1 },
		})
		.fromTo(
			cube,
			{
				rotateY: -90,
			},
			{
				rotateY: 90,
				ease: "power1.inOut",
				duration: 2,
			}
		)
		.fromTo(
			cube.querySelectorAll(".face"),
			{
				color: (j) =>
					"hsl(" +
					((i / n) * 75 + 130) +
					", 67%," +
					100 * [rots[3].a, rots[0].a, rots[1].a][j] +
					"%)",
				opacity: (j) => i * 10 * [rots[0].a, rots[1].a, rots[2].a][j] + "%",
			},
			{
				color: (j) =>
					"hsl(" +
					((i / n) * 75 + 130) +
					", 67%," +
					100 * [rots[0].a, rots[1].a, rots[2].a][j] +
					"%)",
				opacity: (j) => i * [rots[0].a, rots[1].a, rots[2].a][j] + "%",
			},
			0
		)
		.to(
			cube.querySelectorAll(".face"),
			{
				color: (j) =>
					"hsl(" +
					((i / n) * 75 + 130) +
					", 67%," +
					100 * [rots[1].a, rots[2].a, rots[3].a][j] +
					"%)",
			},
			1
		)
		.progress(i / n);
}

gsap
	.timeline()
	.from(
		".tray",
		{ yPercent: -3, duration: 2, ease: "power1.inOut", yoyo: true, repeat: -1 },
		0
	)
	.fromTo(
		".tray",
		{ rotate: -15 },
		{ rotate: 15, duration: 4, ease: "power1.inOut", yoyo: true, repeat: -1 },
		0
	)
	.from(
		".die",
		{ duration: 0.01, opacity: 0, stagger: { each: -0.05, ease: "power1.in" } },
		0
	)
	.to(
		".tray",
		{ scale: 1.2, duration: 2, ease: "power3.inOut", yoyo: true, repeat: -1 },
		0
	);

window.onload = window.onresize = () => {
	const h = n * 56;
	gsap.set(".tray", { height: h });
	gsap.set(".pov", { scale: innerHeight / h });
};

/* LADDER */

// const tl = gsap
//   .timeline({
//     defaults: {
//       duration: 2,
//       yoyo: true,
//       ease: "power2.inOut"
//     }
//   })
//   .fromTo(
//     ".left, .right",
//     {
//       svgOrigin: "640 500",
//       skewY: (i) => [-30, 15][i],
//       scaleX: (i) => [0.6, 0.85][i],
//       x: 200
//     },
//     {
//       skewY: (i) => [-15, 30][i],
//       scaleX: (i) => [0.85, 0.6][i],
//       x: -200
//     }
//   )
//   .play(0.5);

// const tl2 = gsap.timeline();

// document.querySelectorAll("text").forEach((t, i) => {
//   tl2.add(
//     gsap.fromTo(
//       t,
//       {
//         xPercent: -100,
//         x: 700
//       },
//       {
//         duration: 1,
//         xPercent: 0,
//         x: 575,
//         ease: "sine.inOut"
//       }
//     ),
//     (i % 3) * 0.2
//   );
// });

// window.onpointermove = (e) => {
//   tl.pause();
//   tl2.pause();
//   gsap.to([tl, tl2], {
//     duration: 2,
//     ease: "power4",
//     progress: e.x / innerWidth
//   });
// };

// LINES
class Sketch {
	///////////////////////////////
	// contructor
	constructor(options) {
		// constant
		this.time = 0;
		this.isPlaying = true;
		this.mouse = new THREE.Vector2(0, 0);
		this.mouseTarget = new THREE.Vector2(0, 0);

		// scene
		this.scene = new THREE.Scene();

		// renderer
		this.renderer = new THREE.WebGLRenderer({ antialias: true });
		this.renderer.setSize(this.width, this.height);
		this.renderer.setClearColor(0x000000, 1);
		this.renderer.outputEncoding = THREE.sRGBEncoding;

		// container
		this.container = options.dom;
		this.width = this.container.offsetWidth;
		this.height = this.container.offsetHeight;
		this.container.appendChild(this.renderer.domElement);

		// camera
		var frustumSize = 3;
		this.aspect = window.innerWidth / window.innerHeight;
		this.camera = new THREE.OrthographicCamera(
			(frustumSize * this.aspect) / -2,
			(frustumSize * this.aspect) / 2,
			frustumSize / 2,
			frustumSize / -2,
			-1000,
			1000
		);
		this.camera.position.set(0, 0, 2);

		// add object
		this.addObjects();

		// resize
		this.resize();
		this.setupResize();

		// render
		this.render();
	}
	// end of contructor
	///////////////////////////////

	///////////////////////////////
	// resize
	resize() {
		this.width = this.container.offsetWidth;
		this.height = this.container.offsetHeight;
		this.renderer.setSize(this.width, this.height);
		this.camera.aspect = this.width / this.height;

		this.material.uniforms.resolution.value.x = this.width;
		this.material.uniforms.resolution.value.y = this.height;

		this.camera.updateProjectionMatrix();
	}

	setupResize() {
		window.addEventListener("resize", this.resize.bind(this));
	}
	// end of resize
	///////////////////////////////

	///////////////////////////////
	// animation control
	stop() {
		this.isPlaying = false;
	}

	play() {
		if (!this.isPlaying) {
			this.renderer();
			this.isPlaying = true;
		}
	}
	// end of animation control
	///////////////////////////////

	///////////////////////////////
	// render
	render() {
		if (!this.isPlaying) return;
		this.time += 0.05;

		this.material.uniforms.time.value = this.time;
		this.material.uniforms.rotation.value = 0.79
		this.material.uniforms.lineWidth.value = 0.3;
		this.material.uniforms.repeat.value = 10;

		requestAnimationFrame(this.render.bind(this));
		this.renderer.render(this.scene, this.camera);
	} // end of renderer
	///////////////////////////////


	///////////////////////////////
	// add objects
	addObjects() {
		this.material = new THREE.ShaderMaterial({
			extensions: {
				derivatives: "#extension GL_OES_standard_derivatives : enable",
			},
			side: THREE.DoubleSide,
			uniforms: {
				time: { type: "f", value: 0 },
				lineWidth: { type: "f", value: 0 },
				rotation: { type: "f", value: 0 },
				repeat: { type: "f", value: 0 },
				resolution: { type: "v4", value: new THREE.Vector4() },
				uvRate1: {
					value: new THREE.Vector2(1, 1),
				},
			},
			// wireframe: true,
			// transparent: true,
			vertexShader: document.getElementById("vertexShader").textContent,
			fragmentShader: document.getElementById("fragmentShader").textContent,
		});

		this.geometry = new THREE.PlaneGeometry(2.8 * this.aspect, 2.8, 1, 1);

		this.plane = new THREE.Mesh(this.geometry, this.material);
		this.scene.add(this.plane);

		this.box = new THREE.Mesh(
			new THREE.BoxGeometry(1, 1, 0.2).translate(
				0,
				0,
				-0.1
			),
			this.material
		);
		// this.scene.add(this.box);
		this.box.position.z = 1.2;
	}
}
new Sketch({
	dom: document.getElementById("lines"),
});
