import type {EbmlSeekHeadTagType, EbmlTagType} from "konoebml";
import {SeekHeadSchema, type SeekHeadType} from "../schema";
import {isEqual} from "lodash-es";
import {UnreachableOrLogicError} from "@konoplayer/core/errors";
import {SegmentComponentSystemTrait} from "./segment-component";

export const SEEK_ID_KAX_INFO = new Uint8Array([0x15, 0x49, 0xa9, 0x66]);
export const SEEK_ID_KAX_TRACKS = new Uint8Array([0x16, 0x54, 0xae, 0x6b]);
export const SEEK_ID_KAX_CUES = new Uint8Array([0x1c, 0x53, 0xbb, 0x6b]);
export const SEEK_ID_KAX_TAGS = new Uint8Array([0x12, 0x54, 0xc3, 0x67]);

export class SeekSystem extends SegmentComponentSystemTrait<
  EbmlSeekHeadTagType,
  typeof SeekHeadSchema
> {
  override get schema() {
    return SeekHeadSchema;
  }

  seekHeads: SeekHeadType[] = [];
  private offsetToTagMemo: Map<number, EbmlTagType> = new Map();

  memoOffset(tag: EbmlTagType) {
    this.offsetToTagMemo.set(tag.startOffset, tag);
  }

  addSeekHeadTag(tag: EbmlSeekHeadTagType) {
    const seekHead = this.componentFromTag(tag);
    this.seekHeads.push(seekHead);
    return seekHead;
  }

  offsetFromSeekPosition(position: number): number {
    return position + this.segment.contentStartOffset;
  }

  seekTagByStartOffset(
    startOffset: number | undefined
  ): EbmlTagType | undefined {
    return startOffset! >= 0
      ? this.offsetToTagMemo.get(startOffset!)
      : undefined;
  }

  seekOffsetBySeekId(seekId: Uint8Array): number | undefined {
    const seekPosition = this.seekHeads[0]?.Seek?.find((c) =>
      isEqual(c.SeekID, seekId)
    )?.SeekPosition;
    return seekPosition! >= 0
      ? this.offsetFromSeekPosition(seekPosition! as number)
      : undefined;
  }

  seekTagBySeekId(seekId: Uint8Array): EbmlTagType | undefined {
    return this.seekTagByStartOffset(this.seekOffsetBySeekId(seekId));
  }

  get firstClusterOffset() {
    if (!this.segment.firstCluster) {
      throw new UnreachableOrLogicError('first cluster not found');
    }
    return this.segment.firstCluster.startOffset;
  }
}