import { TranslateMode, type Controller } from "./index";
import { UID, PID } from "../persist";
import { AbstractCmdController } from "./abstractCmdController";

export interface Caption {
  beginTime: number;
  endTime: number;
  text: string;
  uid: UID;
  translateText?: string;
}

export interface ParagraphCaption {
  pid: PID;
  beginTime: number;
  endTime: number;
  captions: Caption[];
}

export class CaptionCmdController extends AbstractCmdController {
  paragraphCaptionMap: Record<PID, ParagraphCaption> = {};

  constructor(controller: Controller) {
    super(controller);
    this.listen();
  }

  listen() {
    this.controller.on("translateModeChange", () => {
      this.calcAllCaptions();
      this.controller.emit("captionChange", {});
    });
    this.controller.on("modelValueChange", () => {
      this.calcAllCaptions();
      this.controller.emit("captionChange", {});
    });

    this.controller.on("paragraphChangeSpecific", event => {
      const { addParagraphPids, removeParagraphPids, updateParagraphPids } = event;

      [...addParagraphPids, ...updateParagraphPids].forEach(pid => {
        this.calcCaptionsByPid(pid);
      });

      removeParagraphPids.forEach(pid => {
        delete this.paragraphCaptionMap[pid];
      });

      this.controller.emit("captionChange", {});
    });
  }

  getParagraphCaptionsMap() {
    return this.paragraphCaptionMap;
  }

  getCaptionArray() {
    const pids = this.controller.getPids();
    const captions: Caption[] = [];
    pids.forEach(pid => {
      const paragraphCaption = this.paragraphCaptionMap[pid];
      if (paragraphCaption) {
        captions.push(...paragraphCaption.captions);
      }
    });

    return captions;
  }

  getCaptionByTimestamp(timestamp: number) {
    const pids = Object.keys(this.paragraphCaptionMap);
    let foundCaption: Caption | undefined;
    pids.some(pid => {
      const paragraphCaption = this.paragraphCaptionMap[pid];
      if (timestamp >= paragraphCaption.beginTime && timestamp <= paragraphCaption.endTime) {
        paragraphCaption.captions.some(caption => {
          if (timestamp >= caption.beginTime && timestamp <= caption.endTime) {
            foundCaption = caption;
            return true;
          }
          return false;
        });
        return true;
      }
      return false;
    });
    return foundCaption;
  }

  calcAllCaptions() {
    const pids = this.controller.getPids();
    this.paragraphCaptionMap = {};
    pids.forEach(pid => {
      this.calcCaptionsByPid(pid);
    });
  }

  calcCaptionsByPid(pid: PID) {
    const translateMode = this.controller.getTranslateMode();
    const showTranslate = [TranslateMode.OriginAndTranslate, TranslateMode.TranslateOnly].includes(
      translateMode
    );
    const translates = showTranslate ? this.controller.getTranslate(pid) : undefined;

    const paragraph = this.controller.getParagraph(pid);
    delete this.paragraphCaptionMap[pid];
    if (!paragraph) {
      return;
    }
    const uid = paragraph.uid!;
    const words = this.controller.getWords(pid);
    if (words.length === 0) {
      return;
    }

    const { beginTime } = words[0];
    const { endTime } = words[words.length - 1];

    const paragraphCaption: ParagraphCaption = {
      pid,
      beginTime,
      endTime,
      captions: [],
    };

    if (translates?.translateResult) {
      const { translateResult } = translates;
      translateResult.forEach(translate => {
        let catptionText = "";
        for (let index = 0; index < words.length; index++) {
          const word = words[index];
          if (word.sentenceId === translate.sentenceId) {
            catptionText += word.text;
          }
          if (catptionText && word.sentenceId !== translate.sentenceId) {
            break;
          }
        }
        paragraphCaption.captions.push({
          beginTime: translate.beginTime,
          endTime: translate.endTime,
          text: catptionText,
          translateText: translate.text,
          uid,
        });
      });
    } else {
      let captionBeginTime: number | undefined;
      let catptionText = "";
      words.forEach(word => {
        catptionText += word.text;

        if (!captionBeginTime) {
          captionBeginTime = word.beginTime;
        }

        if (/[，。？,.?](\s)*$/.test(catptionText)) {
          catptionText = catptionText.replace(/[，。？,.?](\s)*$/, "");

          paragraphCaption.captions.push({
            beginTime: captionBeginTime,
            endTime: word.endTime,
            text: catptionText,
            uid,
          });
          catptionText = "";
          captionBeginTime = undefined;
        }
      });
    }
    this.paragraphCaptionMap[pid] = paragraphCaption;
  }
}
