import St from 'gi://St';
import Clutter from 'gi://Clutter';

import * as Params from './params.js';

const SCROLL_TIME = 100;

const WIGGLE_OFFSET = 6;
const WIGGLE_DURATION = 65;
const N_WIGGLES = 3;

/**
 * adjustAnimationTime:
 *
 * @param {number} msecs - time in milliseconds
 *
 * Adjust `msecs` to account for St's enable-animations
 * and slow-down-factor settings
 */
export function adjustAnimationTime(msecs) {
    const settings = St.Settings.get();

    if (!settings.enable_animations)
        return 0;
    return settings.slow_down_factor * msecs;
}

/**
 * Animate scrolling a scrollview until an actor is visible.
 *
 * @param {St.ScrollView} scrollView - the scroll view the actor is in
 * @param {Clutter.Actor} actor - the actor
 */
export function ensureActorVisibleInScrollView(scrollView, actor) {
    const {adjustment} = scrollView.vscroll;
    let [value, lower_, upper, stepIncrement_, pageIncrement_, pageSize] = adjustment.get_values();

    let offset = 0;
    const vfade = scrollView.get_effect('fade');
    if (vfade)
        offset = vfade.fade_margins.top;

    let box = actor.get_allocation_box();
    let y1 = box.y1, y2 = box.y2;

    let parent = actor.get_parent();
    while (parent !== scrollView) {
        if (!parent)
            throw new Error('actor not in scroll view');

        box = parent.get_allocation_box();
        y1 += box.y1;
        y2 += box.y1;
        parent = parent.get_parent();
    }

    if (y1 < value + offset)
        value = Math.max(0, y1 - offset);
    else if (y2 > value + pageSize - offset)
        value = Math.min(upper, y2 + offset - pageSize);
    else
        return;

    adjustment.ease(value, {
        mode: Clutter.AnimationMode.EASE_OUT_QUAD,
        duration: SCROLL_TIME,
    });
}

/**
 * "Wiggles" a clutter actor. A "wiggle" is an animation the moves an actor
 * back and forth on the X axis a specified amount of times.
 *
 * @param {Clutter.Actor} actor - an actor to animate
 * @param {object} params - options for the animation
 * @param {number} params.offset - the offset to move the actor by per-wiggle
 * @param {number} params.duration - the amount of time to move the actor per-wiggle
 * @param {number} params.wiggleCount - the number of times to wiggle the actor
 */
export function wiggle(actor, params) {
    if (!St.Settings.get().enable_animations)
        return;

    params = Params.parse(params, {
        offset: WIGGLE_OFFSET,
        duration: WIGGLE_DURATION,
        wiggleCount: N_WIGGLES,
    });
    actor.translation_x = 0;

    // Accelerate before wiggling
    actor.ease({
        translation_x: -params.offset,
        duration: params.duration,
        mode: Clutter.AnimationMode.EASE_OUT_QUAD,
        onComplete: () => {
            // Wiggle
            actor.ease({
                translation_x: params.offset,
                duration: params.duration,
                mode: Clutter.AnimationMode.LINEAR,
                repeatCount: params.wiggleCount,
                autoReverse: true,
                onComplete: () => {
                    // Decelerate and return to the original position
                    actor.ease({
                        translation_x: 0,
                        duration: params.duration,
                        mode: Clutter.AnimationMode.EASE_IN_QUAD,
                    });
                },
            });
        },
    });
}
