import { inertia, pointer, value, calc, ColdSubscription } from "popmotion";
import { Cancellable } from "@lincode/promiselikes";
import "../assets/style.css";
import TouchLib from "touchlib";
import { extendFunction } from "@lincode/utils";

export class TossableHandle extends Cancellable {
    public constructor(
        cb: () => void,
        public set: (val: number) => void,
        public enable: () => void,
        public disable: () => void
    ) {
        super(cb);
    }
}

type Options = {
    min?: number,
    max?: number,
    start?: number,
    tug?: number,
    power?: number,
    bounceStiffness?: number,
    bounceDamping?: number,
    speed?: number,
    touchTarget: HTMLElement,
    current?: () => number,
    step: (val: number) => void,
    onComplete?: () => void,
    axis?: "x" | "y"
}

export default ({
    min = 0,
    max = 500,
    start = min,
    tug = 0.2,
    power = 0.6,
    bounceStiffness = 400,
    bounceDamping = 20,
    speed = 1,
    touchTarget,
    current,
    step,
    onComplete,
    axis = "x"

}: Options) => {

    const reaction = value(start, step);
    onComplete && (reaction.complete = extendFunction(reaction.complete, onComplete));

    let trackerStart: ColdSubscription | undefined;
    let trackerStop: ColdSubscription | undefined;

    const touch = new TouchLib(touchTarget);

    touchTarget.classList.add("tossable-" + axis);

    const handle0 = touch.on("panstart", e => {
        trackerStart?.stop();
        trackerStop?.stop();

        const isVertical = Math.abs(e.deltaX) < Math.abs(e.deltaY) / 2;
        if ((axis === "x" && isVertical) || (axis === "y" && !isVertical))
            return;

        const reactionStart = current?.() ?? reaction.get() as number;

        const applyOverdrag = (v: number) => {
            if (v < min) return calc.getValueFromProgress(min, v, tug);
            if (v > max) return calc.getValueFromProgress(max, v, tug);
            return v;
        }

        trackerStop = undefined;
        trackerStart = pointer({ preventDefault: false })
            .pipe((v: { x: number, y: number }) => reactionStart + (v[axis] - e[axis]) * speed, applyOverdrag)
            .start(reaction);
    });

    const handle1 = touch.on("panend", () => {
        trackerStart?.stop();
        trackerStop?.stop();

        trackerStart = undefined;
        //@ts-ignore
        trackerStop = inertia({
            min,
            max,
            from: reaction.get(),
            velocity: reaction.getVelocity(),
            power,
            bounceStiffness,
            bounceDamping

        }).start(reaction);
    });

    return new TossableHandle(
        () => {
            handle0.cancel();
            handle1.cancel();

        }, val => {
            trackerStart?.stop();
            trackerStop?.stop();
            trackerStart = undefined;
            trackerStop = undefined;
            reaction.update(val);
        },
        () => touch.enable(),
        () => touch.disable()
    );
}