import { THREE } from "threebox-plugin";
import { point } from "@turf/helpers";
import distance from "@turf/distance";
import { midpoint } from "./utils";
import MercatorCoordinate from "../../geo/mercator_coordinate";

export default class FlyLine {
    constructor(options, scene) {
        this.options = options;
        this.scene = scene;
        this.flyLineAnimationDuration = 3000; // ms
        this.flyLineAnimationNowPercent = 0; // 当前飞线头部运行到总曲线长度的百分比
        this.flyLineLengthPercent = 0.2; // 飞线占总曲线长度的百分比
        this.clock = new THREE.Clock();
        this.init();
    }

    init() {
        const srcGeo = this.options.src;
        const destGeo = this.options.dest;
        const mid = midpoint(srcGeo, destGeo);

        const dist = distance(point(srcGeo), point(destGeo), {
            units: "kilometers",
        });
        const height = dist * 500;

        const mercatorPoints = [
            MercatorCoordinate.fromLngLat(srcGeo, 0),
            MercatorCoordinate.fromLngLat(mid, this.options.height || height),
            MercatorCoordinate.fromLngLat(destGeo, 0),
        ];
        const curve = new THREE.QuadraticBezierCurve3(
            mercatorPoints[0],
            mercatorPoints[1],
            mercatorPoints[2]
        );
        const linepoints = curve.getPoints(this.options.points);
        const percents = new Float32Array(linepoints.length);
        for (let index = 0; index < linepoints.length; index++) {
            percents[index] = index / linepoints.length;
        }

        const lineGeo = new THREE.BufferGeometry().setFromPoints(linepoints);
        // 飞线
        lineGeo.setAttribute(
            "percents",
            new THREE.BufferAttribute(percents, 1)
        );
        const flyLineMat = new THREE.ShaderMaterial({
            uniforms: {
                flyLineAnimationNowPercent: {
                    value: this.flyLineAnimationNowPercent,
                },
                flyLineLengthPercent: {
                    value: this.flyLineLengthPercent,
                },
                color: {
                    value: new THREE.Color(this.options.lineColor),
                },
                size: {
                    value: this.options.size,
                },
            },
            vertexShader: `
                attribute float percents;
                uniform float size;
                uniform float flyLineAnimationNowPercent;
                uniform float flyLineLengthPercent;
                varying float alpha;
                void main() {
                    float head = flyLineAnimationNowPercent;
                    float tail = flyLineAnimationNowPercent - flyLineLengthPercent;
                    if(percents <= head && percents >= tail) {
                        gl_PointSize = size * (1.0 - (head - percents) / (head - tail));
                        alpha = gl_PointSize;
                    } else {
                        gl_PointSize = 0.0;
                    }
                    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
                }
            `,
            fragmentShader: `
                varying float alpha;
                uniform vec3 color;
                void main() {
                    if(alpha == 0.0) { discard; }
                    float dist = distance(gl_PointCoord, vec2(0.5, 0.5));
                    if (dist < 0.5) {
                        gl_FragColor = vec4( color, alpha );
                    } else { discard; }
                }
            `,
        });
        this.flyLine = new THREE.Points(lineGeo, flyLineMat);
        this.scene.add(this.flyLine);

        // 背景线
        const lineMat = new THREE.LineBasicMaterial({
            color: new THREE.Color(this.options.backgroundColor),
            opacity: this.options.backgroundOpacity,
            transparent: true,
        });
        const line = new THREE.Line(lineGeo, lineMat);
        this.scene.add(line);
    }

    animate() {
        let delta = this.clock.getDelta();
        delta *= 1000;
        this.flyLineAnimationNowPercent +=
            delta / this.flyLineAnimationDuration;
        if (this.flyLineAnimationNowPercent >= 1) {
            this.flyLineAnimationNowPercent = 0;
        }
        this.flyLine.material.uniforms.flyLineAnimationNowPercent.value =
            this.flyLineAnimationNowPercent;
    }
}
