import React from "react";
import {
  AbsoluteFill,
  Img,
  Sequence,
  staticFile,
  useCurrentFrame,
  useVideoConfig,
} from "remotion";
import {
  audioFileForSpeech,
  hasAudioForSpeech,
  zundamonReworkStandeeDemoScenario,
} from "./data/zundamon-rework-standee-demo/timing";
import {
  characters,
  type ExpressionEvent,
  type MouthCycleEvent,
  type SpeechEvent,
  type TimelineEvent,
} from "./data/zundamon-rework-standee-demo/script";
import {roundedFontFamily} from "./fonts";
import {
  activeVQChronologicalScenarioSegmentForFrame,
  scheduleVQChronologicalScenario,
  VQLipSyncedStandeeImage,
  VQSpeechOverlay,
  VQWarmGradientBackground,
  type VQMouthShape,
} from "./lib/VQRemotionLib";
import {getMouthForSpeechFrame} from "./lipsync/manifest";

const zundamonAvatar = characters.zundamon.avatar;

const imageDropShadow = "drop-shadow(0 18px 40px rgba(31, 42, 68, 0.22))";

const subtitleOptions = {
  fontFamily: roundedFontFamily,
  fontSize: 34,
  lineHeight: 1.35,
  backgroundColor: "rgba(255, 255, 255, 0.9)",
} as const;

// 用途: タイムラインイベントに共通して使う安定した React key を作る。
// 使用方法: scheduledEvents.map 内でイベントとインデックスを渡し、Sequence の key に使う。
// オプションや引数詳細: 現在のタイムラインイベントはすべて id を持つため、index は呼び出し側の形を保つために受け取る。
const keyForEvent = (event: TimelineEvent, index: number) =>
  `${event.id}-${index}`;

// 用途: 明示口形確認イベントの現在フレームで表示する口形を決める。
// 使用方法: MouthCycleStandee から呼び、イベント尺を mouths の数で均等分割して使う。
// オプションや引数詳細: localFrame はイベント内フレーム、durationInFrames はイベント全体の尺。
const mouthForCycle = (
  event: MouthCycleEvent,
  localFrame: number,
  durationInFrames: number
): VQMouthShape => {
  const framesPerMouth = Math.max(
    1,
    Math.floor(durationInFrames / event.mouths.length)
  );
  const mouthIndex = Math.min(
    event.mouths.length - 1,
    Math.floor(localFrame / framesPerMouth)
  );

  return event.mouths[mouthIndex];
};

const titleStyle: React.CSSProperties = {
  position: "absolute",
  top: 34,
  left: 0,
  right: 0,
  textAlign: "center",
  fontFamily: roundedFontFamily,
  fontSize: 42,
  fontWeight: 800,
  color: "#1f2a44",
};

const labelStyle: React.CSSProperties = {
  position: "absolute",
  top: 94,
  left: 0,
  right: 0,
  textAlign: "center",
  fontFamily: roundedFontFamily,
  fontSize: 26,
  fontWeight: 700,
  color: "#2f6b40",
};

const standeeWrapStyle: React.CSSProperties = {
  position: "absolute",
  inset: "132px 0 116px",
  display: "flex",
  justifyContent: "center",
  alignItems: "center",
};

const StandeeFrame: React.FC<Readonly<{children: React.ReactNode}>> = ({
  children,
}) => <div style={standeeWrapStyle}>{children}</div>;

const ExpressionStandee: React.FC<Readonly<{event: ExpressionEvent}>> = ({
  event,
}) => (
  <StandeeFrame>
    <Img
      src={staticFile(event.imagePath)}
      style={{
        width: zundamonAvatar.imageLayout.width,
        maxHeight: zundamonAvatar.imageLayout.maxHeight,
        objectFit: "contain",
        transform: "scaleX(-1)",
        filter: imageDropShadow,
      }}
    />
  </StandeeFrame>
);

const LipSyncedDefaultStandee: React.FC<Readonly<{mouth: VQMouthShape}>> = ({
  mouth,
}) => (
  <StandeeFrame>
    <VQLipSyncedStandeeImage
      imagePath={zundamonAvatar.imagePath}
      mouthImageDir={zundamonAvatar.mouthImageDir}
      mouth={mouth}
      width={zundamonAvatar.imageLayout.width ?? 540}
      maxHeight={zundamonAvatar.imageLayout.maxHeight ?? 730}
      transform="scaleX(-1)"
      filter={imageDropShadow}
    />
  </StandeeFrame>
);

const TimelineOverlay: React.FC<Readonly<{event: TimelineEvent}>> = ({
  event,
}) => {
  if (event.type !== "say") {
    return null;
  }

  const character = characters[event.character];

  return (
    <VQSpeechOverlay
      speech={event}
      speakerName={character.displayName}
      accentColor={character.avatar.accentColor}
      hasAudio={hasAudioForSpeech}
      getAudioPath={audioFileForSpeech}
      subtitleOptions={subtitleOptions}
      containerStyle={{zIndex: 3}}
    />
  );
};

export const ZundamonReworkStandeeDemo: React.FC = () => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();
  const scheduledEvents = scheduleVQChronologicalScenario(
    zundamonReworkStandeeDemoScenario,
    fps
  );
  const activeSegment = activeVQChronologicalScenarioSegmentForFrame(
    scheduledEvents,
    frame
  );
  const localFrame = activeSegment ? frame - activeSegment.from : 0;
  const activeEvent = activeSegment?.event;
  const activeMouth =
    activeEvent?.type === "say"
      ? getMouthForSpeechFrame(
          (activeEvent as SpeechEvent).id,
          localFrame,
          fps
        )
      : activeEvent?.type === "mouthCycle"
        ? mouthForCycle(
            activeEvent,
            localFrame,
            activeSegment.durationInFrames
          )
        : "rest";
  const activeLabel =
    activeEvent?.type === "expression"
      ? `expression: ${activeEvent.label}`
      : activeEvent?.type === "mouthCycle"
        ? `mouth: ${activeMouth}`
        : "default voicevox-query lip sync";
  const sequences = scheduledEvents.map((scheduledEvent, index) => (
    <Sequence
      key={keyForEvent(scheduledEvent.event, index)}
      from={scheduledEvent.from}
      durationInFrames={scheduledEvent.durationInFrames}
      premountFor={Math.min(fps, scheduledEvent.from)}
    >
      <TimelineOverlay event={scheduledEvent.event} />
    </Sequence>
  ));

  return (
    <AbsoluteFill>
      <VQWarmGradientBackground />
      <div style={titleStyle}>Zundamon Rework Standee Demo</div>
      <div style={labelStyle}>{activeLabel}</div>
      {activeEvent?.type === "expression" ? (
        <ExpressionStandee event={activeEvent} />
      ) : (
        <LipSyncedDefaultStandee mouth={activeMouth} />
      )}
      {sequences}
    </AbsoluteFill>
  );
};
