import { EntitySystem, Matcher, Entity, Time, ECSSystem } from '@esengine/ecs-framework';
import { UIProgressBarComponent } from '../components/widgets/UIProgressBarComponent';
import { UISliderComponent } from '../components/widgets/UISliderComponent';
import { UIButtonComponent } from '../components/widgets/UIButtonComponent';

/**
 * 缓动函数类型
 * Easing function type
 */
export type EasingFunction = (t: number) => number;

/**
 * 预定义缓动函数
 * Predefined easing functions
 */
export const Easing = {
    linear: (t: number) => t,

    // Quad
    easeInQuad: (t: number) => t * t,
    easeOutQuad: (t: number) => t * (2 - t),
    easeInOutQuad: (t: number) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,

    // Cubic
    easeInCubic: (t: number) => t * t * t,
    easeOutCubic: (t: number) => (--t) * t * t + 1,
    easeInOutCubic: (t: number) => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,

    // Quart
    easeInQuart: (t: number) => t * t * t * t,
    easeOutQuart: (t: number) => 1 - (--t) * t * t * t,
    easeInOutQuart: (t: number) => t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t,

    // Quint
    easeInQuint: (t: number) => t * t * t * t * t,
    easeOutQuint: (t: number) => 1 + (--t) * t * t * t * t,
    easeInOutQuint: (t: number) => t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t,

    // Sine
    easeInSine: (t: number) => 1 - Math.cos(t * Math.PI / 2),
    easeOutSine: (t: number) => Math.sin(t * Math.PI / 2),
    easeInOutSine: (t: number) => -(Math.cos(Math.PI * t) - 1) / 2,

    // Expo
    easeInExpo: (t: number) => t === 0 ? 0 : Math.pow(2, 10 * (t - 1)),
    easeOutExpo: (t: number) => t === 1 ? 1 : 1 - Math.pow(2, -10 * t),
    easeInOutExpo: (t: number) => {
        if (t === 0) return 0;
        if (t === 1) return 1;
        if (t < 0.5) return Math.pow(2, 20 * t - 10) / 2;
        return (2 - Math.pow(2, -20 * t + 10)) / 2;
    },

    // Circ
    easeInCirc: (t: number) => 1 - Math.sqrt(1 - t * t),
    easeOutCirc: (t: number) => Math.sqrt(1 - (--t) * t),
    easeInOutCirc: (t: number) => t < 0.5
        ? (1 - Math.sqrt(1 - 4 * t * t)) / 2
        : (Math.sqrt(1 - Math.pow(-2 * t + 2, 2)) + 1) / 2,

    // Back
    easeInBack: (t: number) => {
        const c1 = 1.70158;
        const c3 = c1 + 1;
        return c3 * t * t * t - c1 * t * t;
    },
    easeOutBack: (t: number) => {
        const c1 = 1.70158;
        const c3 = c1 + 1;
        return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
    },
    easeInOutBack: (t: number) => {
        const c1 = 1.70158;
        const c2 = c1 * 1.525;
        return t < 0.5
            ? (Math.pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
            : (Math.pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2;
    },

    // Elastic
    easeInElastic: (t: number) => {
        if (t === 0) return 0;
        if (t === 1) return 1;
        return -Math.pow(2, 10 * t - 10) * Math.sin((t * 10 - 10.75) * ((2 * Math.PI) / 3));
    },
    easeOutElastic: (t: number) => {
        if (t === 0) return 0;
        if (t === 1) return 1;
        return Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * ((2 * Math.PI) / 3)) + 1;
    },
    easeInOutElastic: (t: number) => {
        if (t === 0) return 0;
        if (t === 1) return 1;
        const c5 = (2 * Math.PI) / 4.5;
        return t < 0.5
            ? -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * c5)) / 2
            : (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * c5)) / 2 + 1;
    },

    // Bounce
    easeInBounce: (t: number) => 1 - Easing.easeOutBounce(1 - t),
    easeOutBounce: (t: number) => {
        const n1 = 7.5625;
        const d1 = 2.75;
        if (t < 1 / d1) {
            return n1 * t * t;
        } else if (t < 2 / d1) {
            return n1 * (t -= 1.5 / d1) * t + 0.75;
        } else if (t < 2.5 / d1) {
            return n1 * (t -= 2.25 / d1) * t + 0.9375;
        } else {
            return n1 * (t -= 2.625 / d1) * t + 0.984375;
        }
    },
    easeInOutBounce: (t: number) => t < 0.5
        ? (1 - Easing.easeOutBounce(1 - 2 * t)) / 2
        : (1 + Easing.easeOutBounce(2 * t - 1)) / 2,

    // 简化别名
    easeIn: (t: number) => t * t,
    easeOut: (t: number) => t * (2 - t),
    easeInOut: (t: number) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
};

/**
 * 缓动函数名称映射
 * Easing function name mapping
 */
export type EasingName = keyof typeof Easing;

/**
 * UI 动画系统
 * UI Animation System - Handles value interpolation and animations
 */
@ECSSystem('UIAnimation')
export class UIAnimationSystem extends EntitySystem {
    constructor() {
        // 匹配有任何动画组件的实体
        super(Matcher.empty().any(UIButtonComponent, UIProgressBarComponent, UISliderComponent));
    }

    /**
     * 获取缓动函数
     * Get easing function by name
     */
    public getEasingFunction(name: string): EasingFunction {
        return (Easing as Record<string, EasingFunction>)[name] ?? Easing.linear;
    }

    protected process(entities: readonly Entity[]): void {
        const dt = Time.deltaTime;

        for (const entity of entities) {
            // 处理进度条动画
            this.updateProgressBar(entity, dt);

            // 处理滑块动画
            this.updateSlider(entity, dt);

            // 处理按钮颜色动画
            this.updateButtonColor(entity, dt);
        }
    }

    /**
     * 更新进度条动画
     * Update progress bar animation
     */
    private updateProgressBar(entity: Entity, dt: number): void {
        const progress = entity.getComponent(UIProgressBarComponent);
        if (!progress) return;

        // 如果目标值和显示值不同，进行插值
        if (progress.displayValue !== progress.targetValue) {
            const easingFn = this.getEasingFunction(progress.easing);
            const range = progress.maxValue - progress.minValue;
            const speed = range / progress.transitionDuration;

            const diff = progress.targetValue - progress.displayValue;
            const direction = Math.sign(diff);
            const step = Math.min(Math.abs(diff), speed * dt);

            progress.displayValue += direction * step;

            // 接近目标时直接设置
            if (Math.abs(progress.displayValue - progress.targetValue) < 0.01) {
                progress.displayValue = progress.targetValue;
            }

            progress.value = progress.displayValue;
        }
    }

    /**
     * 更新滑块动画
     * Update slider animation
     */
    private updateSlider(entity: Entity, dt: number): void {
        const slider = entity.getComponent(UISliderComponent);
        if (!slider) return;

        // 如果正在拖拽，直接设置（不做动画）
        if (slider.dragging) {
            slider.displayValue = slider.targetValue;
            slider.value = slider.targetValue;
            return;
        }

        // 平滑插值
        if (slider.displayValue !== slider.targetValue) {
            const range = slider.maxValue - slider.minValue;
            const speed = range / slider.transitionDuration;

            const diff = slider.targetValue - slider.displayValue;
            const direction = Math.sign(diff);
            const step = Math.min(Math.abs(diff), speed * dt);

            slider.displayValue += direction * step;

            if (Math.abs(slider.displayValue - slider.targetValue) < 0.01) {
                slider.displayValue = slider.targetValue;
            }

            slider.value = slider.displayValue;
        }
    }

    /**
     * 更新按钮颜色动画
     * Update button color animation
     */
    private updateButtonColor(entity: Entity, dt: number): void {
        const button = entity.getComponent(UIButtonComponent);
        if (!button) return;

        if (button.currentColor !== button.targetColor) {
            // 颜色插值
            button.currentColor = this.lerpColor(
                button.currentColor,
                button.targetColor,
                Math.min(1, dt / button.transitionDuration)
            );
        }
    }

    /**
     * 颜色线性插值
     * Linear interpolate between two colors
     */
    private lerpColor(from: number, to: number, t: number): number {
        const fromR = (from >> 16) & 0xFF;
        const fromG = (from >> 8) & 0xFF;
        const fromB = from & 0xFF;

        const toR = (to >> 16) & 0xFF;
        const toG = (to >> 8) & 0xFF;
        const toB = to & 0xFF;

        const r = Math.round(fromR + (toR - fromR) * t);
        const g = Math.round(fromG + (toG - fromG) * t);
        const b = Math.round(fromB + (toB - fromB) * t);

        return (r << 16) | (g << 8) | b;
    }

    /**
     * 数值线性插值
     * Linear interpolate between two values
     */
    public lerp(from: number, to: number, t: number): number {
        return from + (to - from) * t;
    }

    /**
     * 应用缓动的插值
     * Interpolate with easing
     */
    public ease(from: number, to: number, t: number, easing: EasingName = 'linear'): number {
        const easingFn = this.getEasingFunction(easing);
        return this.lerp(from, to, easingFn(t));
    }
}
