<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media\Plugin\FFmpegCodec;

use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase;
use Drupal\ffmpeg_media\Service\FFmpegExecutorInterface;
use Drupal\ffmpeg_media\Trait\PluginDefinitionAccessTrait;
use Drupal\ffmpeg_media\ValueObject\ConversionOptions;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

/**
 * Base class for FFmpeg codec plugins.
 */
abstract class FFmpegCodecBase extends PluginBase implements FFmpegCodecInterface, ContainerFactoryPluginInterface {

  use PluginDefinitionAccessTrait;

  /**
   * Constructs an FFmpegCodecBase object.
   *
   * @param array<string, mixed> $configuration
   *   Plugin configuration.
   * @param string $plugin_id
   *   Plugin ID.
   * @param mixed $plugin_definition
   *   Plugin definition.
   * @param \Drupal\ffmpeg_media\Service\FFmpegExecutorInterface $ffmpegExecutor
   *   FFmpeg executor service.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    #[Autowire(service: FFmpegExecutorInterface::class)]
    protected readonly FFmpegExecutorInterface $ffmpegExecutor,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function isAvailable(): bool {
    return $this->ffmpegExecutor->hasEncoder($this->getEncoder());
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function getEncoder(): string {
    return (string) $this->getDefinitionValue('encoder', '');
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function getLabel(): string {
    return (string) $this->getDefinitionValue('label', $this->pluginId);
  }

  /**
   * {@inheritdoc}
   *
   * @return array<int, string>
   *   Supported container formats.
   */
  #[\Override]
  #[\NoDiscard]
  public function getSupportedContainers(): array {
    $containers = $this->getDefinitionValue('supportedContainers', []);
    if (!is_array($containers)) {
      return [];
    }
    return $containers;
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function isHardwareAccelerated(): bool {
    return (bool) $this->getDefinitionValue('hardwareAccelerated', FALSE);
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function supportsTwoPass(): bool {
    return (bool) $this->getDefinitionValue('twoPassSupported', FALSE);
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function getDefaultOptions(): ConversionOptions {
    return new ConversionOptions();
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function getHighQualityOptions(): ConversionOptions {
    return $this->getDefaultOptions()->forHighQuality();
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function getFastEncodingOptions(): ConversionOptions {
    return $this->getDefaultOptions()->forFastEncoding();
  }

  /**
   * Builds common video arguments.
   *
   * @param \Drupal\ffmpeg_media\ValueObject\ConversionOptions $options
   *   Conversion options.
   *
   * @return array<int, string>
   *   Common FFmpeg arguments.
   */
  protected function buildCommonArguments(ConversionOptions $options): array {
    $args = [
      '-pix_fmt', $options->pixelFormat,
      '-threads', (string) $options->threads,
    ];

    if ($options->maxWidth !== NULL || $options->maxHeight !== NULL) {
      $scaleFilter = $this->buildScaleFilter($options->maxWidth, $options->maxHeight);
      $args[] = '-vf';
      $args[] = $scaleFilter;
    }

    return $args;
  }

  /**
   * Builds audio arguments.
   *
   * @param \Drupal\ffmpeg_media\ValueObject\ConversionOptions $options
   *   Conversion options.
   *
   * @return array<int, string>
   *   Audio FFmpeg arguments.
   */
  protected function buildAudioArguments(ConversionOptions $options): array {
    $args = [
      '-c:a', $options->audioCodec,
      '-b:a', $options->audioBitrate,
    ];

    if ($options->audioSampleRate !== NULL) {
      $args[] = '-ar';
      $args[] = (string) $options->audioSampleRate;
    }

    if ($options->audioChannels !== NULL) {
      $args[] = '-ac';
      $args[] = (string) $options->audioChannels;
    }

    return $args;
  }

  /**
   * Builds a scale filter string.
   *
   * @param int|null $maxWidth
   *   Maximum width.
   * @param int|null $maxHeight
   *   Maximum height.
   *
   * @return string
   *   Scale filter string.
   */
  protected function buildScaleFilter(?int $maxWidth, ?int $maxHeight): string {
    $width = $maxWidth !== NULL ? "min($maxWidth,iw)" : 'iw';
    $height = $maxHeight !== NULL ? "min($maxHeight,ih)" : 'ih';

    return "scale='$width':'$height':force_original_aspect_ratio=decrease:force_divisible_by=2";
  }

}
