const float pi = 3.1415927;
float pi2wt = 6.2831854 * (frametime * 24.0);

float GetNoise(vec2 pos) {
	return fract(sin(dot(pos, vec2(12.9898, 4.1414))) * 43758.5453);
}

float Noise2D(vec2 pos) {
    vec2 flr = floor(pos);
    vec2 frc = fract(pos);
    frc = frc * frc * (3.0 - 2.0 * frc);

    float n00 = GetNoise(flr);
    float n01 = GetNoise(flr + vec2(0.0, 1.0));
    float n10 = GetNoise(flr + vec2(1.0, 0.0));
    float n11 = GetNoise(flr + vec2(1.0, 1.0));

    float n0 = mix(n00, n01, frc.y);
    float n1 = mix(n10, n11, frc.y);

    return mix(n0, n1, frc.x) - 0.5;
}

vec3 CalcMove(vec3 pos, float density, float speed, vec2 mult) {

    float DynamicWind = 1.0;
    float windStrength = 1.0;

    #ifdef OVERWORLD
    #ifdef DYNAMIC_WIND
    DynamicWind = 
	(
	worldTime/24000.0 * worldTime/24000.0 * (-worldTime/24000.0 + 1.0) * 4.0 	//afternoon and night wind increase
	+ 0.24*sin(worldTime/24000 + worldDay) 						//sin 1
	+ 0.04*sin((worldTime/24000 + worldDay) * 24.0)					//detail
	+ 0.4*sin((worldTime/24000 + worldDay) * 2.0) + 0.84				//sin 2
	);
    #endif

    windStrength = WIND_WAVING_STRENGTH;
    #endif

    #ifdef NETHER
    windStrength = WIND_WAVING_STRENGTH_NETHER;
    #endif

    #ifdef END
    windStrength = WIND_WAVING_STRENGTH_END;
    #endif

    pos = pos * density + frametime * speed;
    vec3 wave = vec3(Noise2D(pos.yz), Noise2D(pos.xz + 0.333), Noise2D(pos.xy + 0.667)) * (1.0 + 1.2 * rainStrength) * windStrength * DynamicWind;
    return wave * vec3(mult, mult.x);
}

vec3 CalcUnderwaterMove(vec3 pos, float density, float speed, vec2 mult) {
    pos = pos * density + frametime * speed * 0.4;
    vec3 wave = vec3(Noise2D(pos.yz), Noise2D(pos.xz + 0.333), Noise2D(pos.xy + 0.667)) * UNDERWATER_WAVING_STRENGTH;
    return wave * vec3(mult, mult.x);
}

float CalcLilypadMove(vec3 worldpos) {
    float wave = sin(2 * pi * (frametime * 0.7 + worldpos.x * 0.14 + worldpos.z * 0.07)) +
                 sin(2 * pi * (frametime * 0.5 + worldpos.x * 0.10 + worldpos.z * 0.20));
    return wave * 0.025;
}

float CalcLavaMove(vec3 worldpos) {
    float fy = fract(worldpos.y + 0.005);
		
    if (fy > 0.01) {
    float wave = sin(pi * (frametime * 0.7 + worldpos.x * 0.14 + worldpos.z * 0.07)) +
                 sin(pi * (frametime * 0.5 + worldpos.x * 0.10 + worldpos.z * 0.20));
    return wave * 0.025;
    } else return 0.0;
}

vec3 CalcLanternMove(vec3 position) {
    vec3 frc = fract(position);
    frc = vec3(frc.x - 0.5, fract(frc.y - 0.001) - 1.0, frc.z - 0.5);
    vec3 flr = position - frc;
    float offset = flr.x * 2.4 + flr.y * 2.7 + flr.z * 2.2;

    float rmult = pi * 0.016;
    float rx = sin(frametime       + offset) * rmult;
    float ry = sin(frametime * 1.7 + offset) * rmult;
    float rz = sin(frametime * 1.4 + offset) * rmult;
    mat3 rotx = mat3(
               1,        0,        0,
               0,  cos(rx), -sin(rx),
               0,  sin(rx),  cos(rx)
    );
    mat3 roty = mat3(
         cos(ry),        0,  sin(ry),
               0,        1,        0,
        -sin(ry),        0,  cos(ry)
    );
    mat3 rotz = mat3(
         cos(rz), -sin(rz),        0,
         sin(rz),  cos(rz),        0,
               0,        0,        1
    );
    frc = rotx * roty * rotz * frc;
    
    return flr + frc - position;
}

vec3 WavingBlocks(vec3 position, float istopv) {
    vec3 wave = vec3(0.0);
    vec3 worldpos = position + cameraPosition;
    vec2 lightmap = clamp(lmCoord, vec2(0.0), vec2(1.0));

    #ifdef WAVING_PLANT
	//Grass & Dry Grass
    if ((mc_Entity.x == 10100 || mc_Entity.x == 10139) && istopv > 0.9)
        wave += CalcMove(worldpos, 0.35, 1.0, vec2(0.25, 0.06));
    if (mc_Entity.x == 10106 && (istopv > 0.9 || fract(worldpos.y + 0.0675) > 0.01))
        wave += CalcMove(worldpos, 0.35, 1.15, vec2(0.15, 0.06));
    if (mc_Entity.x == 10103 && (istopv > 0.9 || fract(worldpos.y + 0.005) > 0.01))
        wave += CalcMove(worldpos, 0.7, 1.35, vec2(0.12, 0.06));

	//Saplings
    if ((mc_Entity.x == 10121 || mc_Entity.x == 10122) && (istopv > 0.9 || fract(worldpos.y + 0.005) > 0.01))
        wave += CalcMove(worldpos, 0.7, 1.35, vec2(0.12, 0.06)) * 0.32;

    if (mc_Entity.x == 10101 || (mc_Entity.x == 10102.0 && (istopv > 0.9 || fract(worldpos.y+0.005) > 0.01)))
        wave += CalcMove(worldpos, 0.7, 1.25, vec2(0.12, 0.06));

	//Tall Flowers
    if (mc_Entity.x == 10104 || (mc_Entity.x == 10105.0 && (istopv > 0.9 || fract(worldpos.y+0.005) > 0.01)))
        wave += CalcMove(worldpos, 0.7, 1.25, vec2(0.12, 0.06)) * 0.66;

	//Tall Flowers Stiffer || Pale Moss
    if (mc_Entity.x == 10129 || mc_Entity.x == 10107 || (mc_Entity.x == 10108.0 && (istopv > 0.9 || fract(worldpos.y+0.005) > 0.01)))
        wave += CalcMove(worldpos, 0.7, 1.25, vec2(0.12, 0.06)) * 0.4;

	//Cave Vines
    if (mc_Entity.x == 10124)
        wave += CalcMove(worldpos, 0.7, 1.25, vec2(0.12, 0.06)) * 0.2;

	//Big Drip Leaf || Twisting Vines
    if (mc_Entity.x == 10125 || mc_Entity.x == 10191)
        wave += CalcMove(worldpos, 0.7, 1.25, vec2(0.12, 0.06)) * 0.1;

	//Small Drip Leaf
    if (mc_Entity.x == 10126 && (istopv > 0.9 || fract(worldpos.y + 0.005) > 0.01))
        wave += CalcMove(worldpos, 0.7, 1.35, vec2(0.12, 0.06)) * 0.2;

	//Cobweb
    if (mc_Entity.x == 10140)
        wave += CalcMove(worldpos, 0.7, 1.25, vec2(0.12, 0.06)) * 0.3;

	//Vines || Weeping Vines
    if (mc_Entity.x == 10111 || mc_Entity.x == 10190)
        wave += CalcMove(worldpos, 0.35, 1.25, vec2(0.06, 0.06)) * 0.8;
    if (mc_Entity.x == 10112)
        wave.y += CalcLilypadMove(worldpos);

	//Seagrass
    if (mc_Entity.x == 10130 && istopv > 0.9)
        wave += CalcUnderwaterMove(worldpos, 0.35, 1.0, vec2(0.25, 0.06));
    if (mc_Entity.x == 10131 || (mc_Entity.x == 10132.0 && (istopv > 0.9 || fract(worldpos.y+0.005)>0.01)))
        wave += CalcUnderwaterMove(worldpos, 0.7, 1.25, vec2(0.12, 0.06));


    #endif
    #ifdef WAVING_LEAF
    if (mc_Entity.x == 10110 || mc_Entity.x == 10109)
        wave += CalcMove(worldpos, 0.25, 1.0, vec2(0.08, 0.08));
    #endif
    #ifdef WAVING_LIQUID
    if (mc_Entity.x == 10248)
        wave.y += CalcLavaMove(worldpos);
    #endif
    #ifdef WAVING_EXTRA
    if ((mc_Entity.x == 10249 || mc_Entity.x == 10252) && istopv > 0.9)
        wave += CalcMove(worldpos, 1.0, 1.5, vec2(0.0, 0.37));
    if(mc_Entity.x == 10251 || mc_Entity.x == 10253)
		wave += CalcLanternMove(worldpos);
    #endif

    #ifdef OVERWORLD
    #ifndef WINDY_CAVES
    if(mc_Entity.x >= 10100 && mc_Entity.x <= 10129 || mc_Entity.x == 10140 || mc_Entity.x == 10251 || mc_Entity.x == 10253 || mc_Entity.x == 10190 || mc_Entity.x == 10191)
    wave *= lightmap.y;
    #endif
    #endif

    position += wave;

    return position;
}