export interface Config {
  availableTimelines: string[];
}

export interface Room {
  id: number;
  userCount: number;
  isLeader: boolean;
  running: boolean;
  startTime?: number;
  seekTime: number;
  timelineName: string;
  readyToParticipate: boolean;
  speedFactor: number;
  ticker: string[];
  users?: { id: string; readyToParticipate: boolean }[];
}

export class RoomOptions {
  seekTime: number;
  timelineName: string;

  constructor(seekTime: number, timelineName: string) {
    this.seekTime = seekTime;
    this.timelineName = timelineName;
  }
}

export class Timeline {
  name: string;
  songFile: string;
  feed: TimelineItem[];

  constructor(obj: any) {
    this.name = obj.name;
    this.songFile = obj.songFile;
    this.feed = obj.feed;

    this.feed = this.feed.sort((a, b) => a.timestamp - b.timestamp);

    // Add string ids to the feed to uniquely identify them in the various
    // reactive components.
    for (let i = 0; i < this.feed.length; i++) {
      this.feed[i].id = i.toString();
      for (let j = 0; j < this.feed[i].events.length; j++) {
        this.feed[i].events[j].id = `${i}:${j}`;
      }
    }
  }

  getTotalShotCount(): number {
    let maxShot = 0;

    for (const item of this.feed) {
      for (const event of item.events) {
        if (event.type === "shot") {
          maxShot = Math.max(
            maxShot,
            (event as TimestampEventShot).shotCount || 0
          );
        }
      }
    }

    return maxShot;
  }

  itemAfterTime(time: number, type?: EventType): TimelineItem | undefined {
    const feedToSearch = type
      ? this.feed.filter((i) => i.events.some((j) => j.type === type))
      : this.feed;
    return feedToSearch.find((item) => item.timestamp * 1000 > time);
  }

  itemBeforeTime(time: number, type?: EventType): TimelineItem | undefined {
    const feedToSearch = type
      ? this.feed.filter((i) => i.events.some((j) => j.type === type))
      : this.feed;
    return feedToSearch.reverse().find((item) => item.timestamp * 1000 < time);
  }

  eventAfterTime(time: number, type: "shot"): TimestampEventShot | undefined;
  eventAfterTime(time: number, type: EventType): TimestampEvent | undefined {
    const item = this.itemAfterTime(time, type);

    if (!item || !item.events.length) {
      return undefined;
    }

    return item.events.find((ev) => ev.type === type);
  }

  eventBeforeTime(time: number, type: "shot"): TimestampEventShot | undefined;
  eventBeforeTime(time: number, type: EventType): TimestampEvent | undefined {
    const item = this.itemBeforeTime(time, type);

    if (!item || !item.events.length) {
      return undefined;
    }

    return item.events.find((ev) => ev.type === type);
  }
}

export interface TimelineItem {
  id: string;
  timestamp: number;
  events: TimestampEvent[];
}

export const EVENT_PRIORITY: EventType[] = ["shot", "talk", "time", "song"];
export type EventType = "talk" | "shot" | "song" | "time";

interface TimestampEventBase {
  id: string;
  type: EventType;
  text: string[];
}

interface TimestampEventShot extends TimestampEventBase {
  type: "shot";
  shotCount: number;
}

export type TimestampEvent = TimestampEventBase | TimestampEventShot;
