export type InterpolatableValue = number | number[];

interface Keyframe {
  time: number;
  value: InterpolatableValue;
}

export class Track {
  private readonly keyframes: Keyframe[];

  constructor(keyframes: Keyframe[]) {
    this.keyframes = keyframes.sort((a, b) => a.time - b.time);
  }

  interpolate(time: number): InterpolatableValue {
    const { keyframes } = this;
    const lastIndex = keyframes.length - 1;

    if (time <= keyframes[0].time) return keyframes[0].value;
    if (time >= keyframes[lastIndex].time) return keyframes[lastIndex].value;

    const nextIndex = keyframes.findIndex(kf => kf.time > time);
    const prevIndex = nextIndex - 1;

    const [prevKf, nextKf] = [keyframes[prevIndex], keyframes[nextIndex]];
    const t = (time - prevKf.time) / (nextKf.time - prevKf.time);

    return this.interpolateValues(prevKf.value, nextKf.value, t);
  }

  private interpolateValues(start: InterpolatableValue, end: InterpolatableValue, t: number): InterpolatableValue {
    if (Array.isArray(start) && Array.isArray(end)) {
      return start.map((v, i) => v + t * (end[i] - v));
    }
    if (typeof start === 'number' && typeof end === 'number') {
      return start + t * (end - start);
    }
    throw new Error('Mismatched value types in keyframes');
  }
}

export class Timeline {
  private readonly tracks: Record<string, Track>;
  private readonly duration: number;
  private loop: boolean;

  constructor(tracks: Record<string, Track>, duration: number, loop: boolean = false) {
    this.tracks = tracks;
    this.duration = duration;
    this.loop = loop;
  }

  setLoop(loop: boolean): void {
    this.loop = loop;
  }

  update(time: number): Record<string, InterpolatableValue> {
    let adjustedTime = time;
    
    if (this.loop) {
      adjustedTime = time % this.duration;
    } else {
      adjustedTime = Math.min(time, this.duration);
    }

    return Object.fromEntries(
      Object.entries(this.tracks).map(([key, track]) => [key, track.interpolate(adjustedTime)])
    );
  }
}