<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media\Plugin\FFmpegCodec;

use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\ffmpeg_media\Attribute\FFmpegCodec;
use Drupal\ffmpeg_media\ValueObject\ConversionOptions;

/**
 * H.264/AVC codec plugin.
 */
#[FFmpegCodec(
  id: 'h264',
  label: new TranslatableMarkup('H.264 (AVC)'),
  description: new TranslatableMarkup('Most compatible video codec for web playback.'),
  encoder: 'libx264',
  supportedContainers: ['mp4', 'mkv', 'mov', 'flv', 'hls', 'dash', 'whip', 'rtp'],
  hardwareAccelerated: FALSE,
  twoPassSupported: TRUE,
  weight: 0,
)]
final class H264Codec extends FFmpegCodecBase {

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function buildArguments(ConversionOptions $options): array {
    $args = [
      '-c:v', 'libx264',
      '-preset', $this->getPresetName($options->preset),
    ];

    // Add rate control settings.
    if ($options->rateControl !== NULL) {
      $args = array_merge($args, $options->rateControl->buildArguments('h264'));
    }
    else {
      // Default to CRF mode.
      $args[] = '-crf';
      $args[] = (string) $options->crf;
    }

    // Add GOP/keyframe settings.
    if ($options->gopSettings !== NULL) {
      $args = array_merge($args, $options->gopSettings->buildArguments('h264'));
    }

    // Add common video arguments.
    $args = array_merge($args, $this->buildCommonArguments($options));

    // Add audio arguments.
    $args = array_merge($args, $this->buildAudioArguments($options));

    // Add extra arguments.
    foreach ($options->extraArgs as $arg) {
      $args[] = $arg;
    }

    return $args;
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function getDefaultOptions(): ConversionOptions {
    return new ConversionOptions(
      crf: 23,
      preset: 6,
      threads: 0,
      audioBitrate: '128k',
      audioCodec: 'libopus',
      pixelFormat: 'yuv420p',
    );
  }

  /**
   * Converts numeric preset to x264 preset name.
   *
   * @param int $preset
   *   Numeric preset (0-9).
   *
   * @return string
   *   x264 preset name.
   */
  protected function getPresetName(int $preset): string {
    $presets = [
      0 => 'placebo',
      1 => 'veryslow',
      2 => 'slower',
      3 => 'slow',
      4 => 'medium',
      5 => 'fast',
      6 => 'faster',
      7 => 'veryfast',
      8 => 'superfast',
      9 => 'ultrafast',
    ];

    return $presets[$preset] ?? 'medium';
  }

}
