/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.media3.exoplayer;

import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotEmpty;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static java.lang.annotation.ElementType.TYPE_USE;

import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.media3.common.ColorInfo;
import androidx.media3.common.Format;
import androidx.media3.common.util.UnstableApi;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * The result of an evaluation to determine whether a decoder can be reused for a new input format.
 */
@UnstableApi
public final class DecoderReuseEvaluation {

  /** Possible outcomes of the evaluation. */
  @Documented
  @Retention(RetentionPolicy.SOURCE)
  @Target(TYPE_USE)
  @IntDef({
    REUSE_RESULT_NO,
    REUSE_RESULT_YES_WITH_FLUSH,
    REUSE_RESULT_YES_WITH_RECONFIGURATION,
    REUSE_RESULT_YES_WITHOUT_RECONFIGURATION
  })
  public @interface DecoderReuseResult {}
  /** The decoder cannot be reused. */
  public static final int REUSE_RESULT_NO = 0;
  /** The decoder can be reused, but must be flushed. */
  public static final int REUSE_RESULT_YES_WITH_FLUSH = 1;
  /**
   * The decoder can be reused. It does not need to be flushed, but must be reconfigured by
   * prefixing the next input buffer with the new format's configuration data.
   */
  public static final int REUSE_RESULT_YES_WITH_RECONFIGURATION = 2;
  /** The decoder can be kept. It does not need to be flushed and no reconfiguration is required. */
  public static final int REUSE_RESULT_YES_WITHOUT_RECONFIGURATION = 3;

  /** Possible reasons why reuse is not possible. */
  @Documented
  @Retention(RetentionPolicy.SOURCE)
  @Target(TYPE_USE)
  @IntDef(
      flag = true,
      value = {
        DISCARD_REASON_REUSE_NOT_IMPLEMENTED,
        DISCARD_REASON_WORKAROUND,
        DISCARD_REASON_APP_OVERRIDE,
        DISCARD_REASON_MIME_TYPE_CHANGED,
        DISCARD_REASON_OPERATING_RATE_CHANGED,
        DISCARD_REASON_INITIALIZATION_DATA_CHANGED,
        DISCARD_REASON_DRM_SESSION_CHANGED,
        DISCARD_REASON_MAX_INPUT_SIZE_EXCEEDED,
        DISCARD_REASON_VIDEO_MAX_RESOLUTION_EXCEEDED,
        DISCARD_REASON_VIDEO_RESOLUTION_CHANGED,
        DISCARD_REASON_VIDEO_ROTATION_CHANGED,
        DISCARD_REASON_VIDEO_COLOR_INFO_CHANGED,
        DISCARD_REASON_AUDIO_CHANNEL_COUNT_CHANGED,
        DISCARD_REASON_AUDIO_SAMPLE_RATE_CHANGED,
        DISCARD_REASON_AUDIO_ENCODING_CHANGED
      })
  public @interface DecoderDiscardReasons {}

  /** Decoder reuse is not implemented. */
  public static final int DISCARD_REASON_REUSE_NOT_IMPLEMENTED = 1 << 0;
  /** Decoder reuse is disabled by a workaround. */
  public static final int DISCARD_REASON_WORKAROUND = 1 << 1;
  /** Decoder reuse is disabled by overriding behavior in application code. */
  public static final int DISCARD_REASON_APP_OVERRIDE = 1 << 2;
  /** The sample MIME type is changing. */
  public static final int DISCARD_REASON_MIME_TYPE_CHANGED = 1 << 3;
  /** The codec's operating rate is changing. */
  public static final int DISCARD_REASON_OPERATING_RATE_CHANGED = 1 << 4;
  /** The format initialization data is changing. */
  public static final int DISCARD_REASON_INITIALIZATION_DATA_CHANGED = 1 << 5;
  /** The new format may exceed the decoder's configured maximum sample size, in bytes. */
  public static final int DISCARD_REASON_MAX_INPUT_SIZE_EXCEEDED = 1 << 6;
  /** The DRM session is changing. */
  public static final int DISCARD_REASON_DRM_SESSION_CHANGED = 1 << 7;
  /** The new format may exceed the decoder's configured maximum resolution. */
  public static final int DISCARD_REASON_VIDEO_MAX_RESOLUTION_EXCEEDED = 1 << 8;
  /** The video resolution is changing. */
  public static final int DISCARD_REASON_VIDEO_RESOLUTION_CHANGED = 1 << 9;
  /** The video rotation is changing. */
  public static final int DISCARD_REASON_VIDEO_ROTATION_CHANGED = 1 << 10;
  /** The video {@link ColorInfo} is changing. */
  public static final int DISCARD_REASON_VIDEO_COLOR_INFO_CHANGED = 1 << 11;
  /** The audio channel count is changing. */
  public static final int DISCARD_REASON_AUDIO_CHANNEL_COUNT_CHANGED = 1 << 12;
  /** The audio sample rate is changing. */
  public static final int DISCARD_REASON_AUDIO_SAMPLE_RATE_CHANGED = 1 << 13;
  /** The audio encoding is changing. */
  public static final int DISCARD_REASON_AUDIO_ENCODING_CHANGED = 1 << 14;

  /** The name of the decoder. */
  public final String decoderName;

  /** The {@link Format} for which the decoder was previously configured. */
  public final Format oldFormat;

  /** The new {@link Format} being evaluated. */
  public final Format newFormat;

  /** The {@link DecoderReuseResult result} of the evaluation. */
  public final @DecoderReuseResult int result;

  /**
   * {@link DecoderDiscardReasons Reasons} why the decoder cannot be reused. Always {@code 0} if
   * reuse is possible. May also be {code 0} if reuse is not possible for an unspecified reason.
   */
  public final @DecoderDiscardReasons int discardReasons;

  /**
   * @param decoderName The name of the decoder.
   * @param oldFormat The {@link Format} for which the decoder was previously configured.
   * @param newFormat The new {@link Format} being evaluated.
   * @param result The {@link DecoderReuseResult result} of the evaluation.
   * @param discardReasons One or more {@link DecoderDiscardReasons reasons} why the decoder cannot
   *     be reused, or {@code 0} if reuse is possible.
   */
  public DecoderReuseEvaluation(
      String decoderName,
      Format oldFormat,
      Format newFormat,
      @DecoderReuseResult int result,
      @DecoderDiscardReasons int discardReasons) {
    checkArgument(result == REUSE_RESULT_NO || discardReasons == 0);
    this.decoderName = checkNotEmpty(decoderName);
    this.oldFormat = checkNotNull(oldFormat);
    this.newFormat = checkNotNull(newFormat);
    this.result = result;
    this.discardReasons = discardReasons;
  }

  @Override
  public boolean equals(@Nullable Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null || getClass() != obj.getClass()) {
      return false;
    }
    DecoderReuseEvaluation other = (DecoderReuseEvaluation) obj;
    return result == other.result
        && discardReasons == other.discardReasons
        && decoderName.equals(other.decoderName)
        && oldFormat.equals(other.oldFormat)
        && newFormat.equals(other.newFormat);
  }

  @Override
  public int hashCode() {
    int hashCode = 17;
    hashCode = 31 * hashCode + result;
    hashCode = 31 * hashCode + discardReasons;
    hashCode = 31 * hashCode + decoderName.hashCode();
    hashCode = 31 * hashCode + oldFormat.hashCode();
    hashCode = 31 * hashCode + newFormat.hashCode();
    return hashCode;
  }
}
