package org.thoughtcrime.securesms.video;

import android.media.MediaDataSource;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import org.thoughtcrime.securesms.crypto.AttachmentSecret;
import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

/**
 * Create via {@link EncryptedMediaDataSource}.
 * <p>
 * A {@link MediaDataSource} that points to an encrypted file.
 * <p>
 * It is "modern" compared to the {@link ClassicEncryptedMediaDataSource}. And "modern" refers to
 * the presence of a random part of the key supplied in the constructor.
 */
@RequiresApi(23)
final class ModernEncryptedMediaDataSource extends MediaDataSource {

  private final AttachmentSecret attachmentSecret;
  private final File             mediaFile;
  private final byte[]           random;
  private final long             length;

  ModernEncryptedMediaDataSource(@NonNull AttachmentSecret attachmentSecret, @NonNull File mediaFile, @Nullable byte[] random, long length) {
    this.attachmentSecret = attachmentSecret;
    this.mediaFile        = mediaFile;
    this.random           = random;
    this.length           = length;
  }

  @Override
  public int readAt(long position, byte[] bytes, int offset, int length) throws IOException {
    if (position >= this.length) {
      return -1;
    }

    try (InputStream inputStream = createInputStream(position)) {
      int totalRead = 0;

      while (length > 0) {
        int read = inputStream.read(bytes, offset, length);

        if (read == -1) {
          if (totalRead == 0) {
            return -1;
          } else {
            return totalRead;
          }
        }

        length    -= read;
        offset    += read;
        totalRead += read;
      }

      return totalRead;
    }
  }

  @Override
  public long getSize() {
    return length;
  }

  @Override
  public void close() {
  }

  private InputStream createInputStream(long position) throws IOException {
    if (random == null) {
      return ModernDecryptingPartInputStream.createFor(attachmentSecret, mediaFile, position);
    } else {
      return ModernDecryptingPartInputStream.createFor(attachmentSecret, random, mediaFile, position);
    }
  }
}
