import type {
	AnimationPath,
	ElementAnimations,
} from "@/animation/types";
import { formatLinearRgba, parseColorToLinearRgba } from "@/params";
import type { ParamValue } from "@/params";
import { isCompositeChannelData, isLeafChannelData } from "./channel-data";
import {
	getChannelValueAtTime,
	isScalarChannel,
} from "./interpolation";

export function getElementLocalTime({
	timelineTime,
	elementStartTime,
	elementDuration,
}: {
	timelineTime: number;
	elementStartTime: number;
	elementDuration: number;
}): number {
	const localTime = timelineTime - elementStartTime;
	if (localTime <= 0) {
		return 0;
	}

	if (localTime >= elementDuration) {
		return elementDuration;
	}

	return localTime;
}

export function resolveAnimationPathValueAtTime({
	animations,
	propertyPath,
	localTime,
	fallbackValue,
}: {
	animations: ElementAnimations | undefined;
	propertyPath: AnimationPath;
	localTime: number;
	fallbackValue: number;
}): number;
export function resolveAnimationPathValueAtTime({
	animations,
	propertyPath,
	localTime,
	fallbackValue,
}: {
	animations: ElementAnimations | undefined;
	propertyPath: AnimationPath;
	localTime: number;
	fallbackValue: string;
}): string;
export function resolveAnimationPathValueAtTime({
	animations,
	propertyPath,
	localTime,
	fallbackValue,
}: {
	animations: ElementAnimations | undefined;
	propertyPath: AnimationPath;
	localTime: number;
	fallbackValue: boolean;
}): boolean;
export function resolveAnimationPathValueAtTime({
	animations,
	propertyPath,
	localTime,
	fallbackValue,
}: {
	animations: ElementAnimations | undefined;
	propertyPath: AnimationPath;
	localTime: number;
	fallbackValue: ParamValue;
}): ParamValue;
export function resolveAnimationPathValueAtTime({
	animations,
	propertyPath,
	localTime,
	fallbackValue,
}: {
	animations: ElementAnimations | undefined;
	propertyPath: AnimationPath;
	localTime: number;
	fallbackValue: ParamValue;
}): ParamValue {
	const data = animations?.[propertyPath];
	if (!data) {
		return fallbackValue;
	}
	if (isLeafChannelData(data)) {
		if (typeof fallbackValue === "number") {
			return getChannelValueAtTime({
				channel: isScalarChannel(data) ? data : undefined,
				time: localTime,
				fallbackValue,
			});
		}
		if (typeof fallbackValue === "string" || typeof fallbackValue === "boolean") {
			return getChannelValueAtTime({
				channel: !isScalarChannel(data) ? data : undefined,
				time: localTime,
				fallbackValue,
			});
		}
		return fallbackValue;
	}
	if (!isCompositeChannelData(data)) {
		return fallbackValue;
	}

	if (
		typeof fallbackValue !== "string" ||
		!("r" in data) ||
		!("g" in data) ||
		!("b" in data) ||
		!("a" in data)
	) {
		return fallbackValue;
	}

	const fallbackComponents = parseColorToLinearRgba({ color: fallbackValue });
	if (fallbackComponents === null) {
		return fallbackValue;
	}

	return formatLinearRgba({
		color: {
			r: getChannelValueAtTime({
				channel: data.r && isScalarChannel(data.r) ? data.r : undefined,
				time: localTime,
				fallbackValue: fallbackComponents.r,
			}),
			g: getChannelValueAtTime({
				channel: data.g && isScalarChannel(data.g) ? data.g : undefined,
				time: localTime,
				fallbackValue: fallbackComponents.g,
			}),
			b: getChannelValueAtTime({
				channel: data.b && isScalarChannel(data.b) ? data.b : undefined,
				time: localTime,
				fallbackValue: fallbackComponents.b,
			}),
			a: getChannelValueAtTime({
				channel: data.a && isScalarChannel(data.a) ? data.a : undefined,
				time: localTime,
				fallbackValue: fallbackComponents.a,
			}),
		},
	});
}
